# 42P20 — Windowing error

> Source-backed reference for PostgreSQL SQLSTATE 42P20.
---

# 42P20 — Windowing error

## At a glance {#at-a-glance}

`42P20` (**windowing_error**) Window definitions constrain both calls and frame boundaries.

<!-- BEGIN SQLSTATE FACTS: generated by scripts/generate.py; do not edit -->

| Field | Value |
| --- | --- |
| SQLSTATE | `42P20` |
| Condition | `windowing_error` |
| Status | `active` |
| Known present by | `8.4.0` |
| Locked snapshots | `9.0.23, 9.1.24, 9.2.24, 9.3.25, 9.4.26, 9.5.25, 9.6.24, 10.23, 11.22, 12.22, 13.23, 14.24, 15.19, 16.15, 17.11, 18.6, 19beta3` |
| Macros | `ERRCODE_WINDOWING_ERROR` |
| Aliases | `—` |

<!-- source facts: data/errcodes/42P20.json -->
<!-- END SQLSTATE FACTS -->

## Meaning {#meaning}

The fixed core uses `42P20` for window-function placement or nesting errors and for invalid window-definition structure. Parser grammar rejects impossible frame start/end combinations; parse analysis rejects a window function in a clause that cannot contain it or another window function; named-window transformation rejects conflicting inheritance. A legal frame can still be empty for a particular row, so an empty result is not by itself a `42P20` signal.

## Diagnosis {#diagnosis}

Classify the primary message before changing SQL. A frame message points to `UNBOUNDED` direction or start/end ordering; `RANGE` with an offset requires exactly one `ORDER BY` column and `GROUPS` requires an `ORDER BY` clause. A placement message names a clause such as `WHERE`, `GROUP BY`, `JOIN`, `RETURNING`, or a window definition, while the nested-call message means a window expression appears inside another window expression. Named-window messages distinguish duplicate definitions, attempts to override copied `PARTITION BY` or `ORDER BY`, and copying a window that already has a frame; `OVER foo` and `OVER (foo)` therefore have different inheritance behavior. The executor separately reports negative `ROWS` or `GROUPS` frame offsets as SQLSTATE `22013` (`invalid_preceding_or_following_size`), and null offsets as `22004`; those are not `42P20`.

## Response {#response}

Move a nested calculation to an outer query level, or move the window expression out of the named clause identified by the message. Repair only the relevant frame grammar, add the required ordering for `RANGE`/`GROUPS`, and define one named window without conflicting overrides. Do not change a valid frame merely because it produces no rows for one current row. If this `ERROR` occurs inside an explicit transaction, issue `ROLLBACK` or `ROLLBACK TO` a savepoint created before the statement before retrying; autocommit can submit the corrected query after the failed statement returns to idle.

## Messages {#messages}

Representative fixed-source messages include:

- **ERROR** message: `window function calls cannot be nested`
- **ERROR** message: `frame start cannot be UNBOUNDED FOLLOWING`
- **ERROR** message: `frame starting from following row cannot end with current row`
- **ERROR** message: `frame end cannot be UNBOUNDED PRECEDING`
- **ERROR** message: `frame starting from current row cannot have preceding rows`
- **ERROR** message: `frame starting from following row cannot have preceding rows`
- **ERROR** message: `window functions are not allowed in %s`
- **ERROR** message: `window "%s" is already defined`
- **ERROR** message: `cannot override PARTITION BY clause of window "%s"`
- **ERROR** message: `cannot override ORDER BY clause of window "%s"`
- **ERROR** message: `cannot copy window "%s" because it has a frame clause`
- **ERROR** message: `cannot copy window "%s" because it has a frame clause`; HINT: `Omit the parentheses in this OVER clause.`
- **ERROR** message: `RANGE with offset PRECEDING/FOLLOWING requires exactly one ORDER BY column`
- **ERROR** message: `GROUPS mode requires an ORDER BY clause`

Placeholders are filled by the actual object, column, or parameter.

## Versions {#versions}

The locked catalogue shows this condition by PostgreSQL 8.4.0; behavior here is fixed to PostgreSQL 18.6 source. Catalogue presence is a range boundary, not proof that every message or feature began in that release.

## Related {#related}

- [`42P19`](../42p19/)
- [`42803`](../42803/)

## Sources {#sources}

Source messages, line anchors, and evidence limits are recorded in [authored evidence](../data/evidence/42p20.json).

- `src/backend/executor/execExpr.c:1155-1157` ([fixed source](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/executor/execExpr.c#L1155))
- `src/backend/parser/gram.y:16499-16544` ([fixed source](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/parser/gram.y#L16499))
- `src/backend/parser/parse_agg.c:1041-1046` ([fixed source](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/parser/parse_agg.c#L1041))
- `src/backend/parser/parse_clause.c:2790-2947` ([fixed source](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/parser/parse_clause.c#L2790))
- `src/backend/executor/nodeWindowAgg.c:2139-2210` ([fixed source](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/executor/nodeWindowAgg.c#L2139))
