PostgreSQL raises ERROR: invalid column reference with SQLSTATE 42P10 and condition name invalid_column_reference when a column reference appears in a context where it is not permitted. The most common trigger is an invalid column reference in a window function definition — for example, referencing a column in a way that violates the scoping rules for OVER (...) clauses.
What This Error Means
SQLSTATE 42P10 belongs to PostgreSQL error class 42 — "Syntax Error or Access Rule Violation". Errors in this class are caught at parse or planning time, before any rows are read or written, so they do not leave a transaction in an aborted state by themselves. The session remains usable and you can retry a corrected query immediately.
The error surfaces when the parser or planner validates a column reference and finds it in a position where it cannot be resolved under PostgreSQL's scoping rules. The most common case is a window function whose OVER clause references a column from an outer query level — PostgreSQL does not allow outer references inside PARTITION BY or ORDER BY within a window definition. Related violations include referencing a column alias defined in the same SELECT list inside a WHERE clause (a separate but conceptually similar scoping violation).
Because the check happens at query compile time, no partial execution occurs. The error is deterministic: the same query will always fail until the column reference is corrected.
Common Causes
Outer-query column referenced inside a window function's
OVERclause. PostgreSQL prohibits a correlated reference to an outer query's column insidePARTITION BYorORDER BYof a window function. This is a stricter rule than what some other databases allow.Column alias used in
WHEREorHAVINGof the same query level. PostgreSQL resolvesWHEREandHAVINGbefore theSELECTlist, so an alias defined inSELECTis not yet visible there. Attempting to reference it triggers a related invalid-reference error.Window function reference used inside another window function. PostgreSQL does not allow nesting window function calls. Wrapping one
OVERexpression inside another's arguments results in42P10.Using a
PARTITION BYorORDER BYcolumn that belongs to an outerFROMscope in a subquery containing the window function. This is a variation of cause 1, encountered when a lateral or correlated subquery introduces a window function.
How to Fix invalid_column_reference
Move the correlated column to the subquery's
SELECTlist, then use it in the outer query.Instead of:
-- Fails: outer.dept_id referenced inside window OVER clause SELECT e.name, (SELECT row_number() OVER (PARTITION BY outer.dept_id ORDER BY salary DESC) FROM employees e2 WHERE e2.dept_id = outer.dept_id) AS rn FROM employees outer;Rewrite using a lateral join or a CTE that brings the partition column into scope:
SELECT e.name, sub.rn FROM employees e CROSS JOIN LATERAL ( SELECT row_number() OVER (PARTITION BY e.dept_id ORDER BY salary DESC) AS rn FROM employees e2 WHERE e2.dept_id = e.dept_id LIMIT 1 ) sub;Replace the window function nesting with a derived table or CTE.
Instead of nesting window calls, compute the inner window result in a CTE and reference it in the outer query:
-- Fails: window function inside another window function argument -- SELECT sum(row_number() OVER (...)) OVER (...) ... WITH ranked AS ( SELECT id, salary, row_number() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rn FROM employees ) SELECT dept_id, sum(rn) OVER (ORDER BY dept_id) AS cumulative_rn FROM ranked;Avoid referencing
SELECT-list aliases inWHEREorHAVING; repeat the expression instead.-- Fails: alias 'annual' not yet defined when WHERE is evaluated -- SELECT salary * 12 AS annual FROM employees WHERE annual > 60000; -- Correct: repeat the expression in WHERE SELECT salary * 12 AS annual FROM employees WHERE salary * 12 > 60000;Use a subquery or CTE when you must filter on a computed window value.
Window functions cannot appear in
WHEREorHAVINGat all (that is a separate42P10/42803violation). Wrap the windowed query in a CTE or derived table:WITH ranked AS ( SELECT *, row_number() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rn FROM employees ) SELECT * FROM ranked WHERE rn = 1;
Additional Information
- SQLSTATE
42P10is a PostgreSQL-specific code (thePprefix denotes a PostgreSQL extension beyond the SQL standard). It is not present in MySQL or other databases. - Related error codes in class
42include42803(grouping_error, raised when a non-aggregated column appears in aSELECTwithGROUP BY) and42P20(windowing_error, raised for other window function violations such as a window function in an invalid clause position). - Most PostgreSQL client drivers (psycopg2, asyncpg, JDBC, node-postgres) surface this as a
ProgrammingErroror equivalent, with the SQLSTATE code available on the exception object. Checke.pgcode == '42P10'in Python ore.getSQLState()in JDBC. - Because
42P10is a compile-time error, it will appear in application logs at query preparation time if you use prepared statements, not at execution time. This can make it visible earlier in the request lifecycle than runtime errors. - The error was present well before PostgreSQL 9.0 and behavior has not changed meaningfully across recent versions.
Frequently Asked Questions
Why does PostgreSQL forbid outer-column references inside window OVER clauses?
PostgreSQL's window function implementation evaluates the entire OVER clause in the context of the query level where the window function appears. An outer-query column is not in scope at that level, so the reference cannot be resolved. This restriction stems from the SQL standard's scoping model for window specifications and is enforced at parse/planning time.
Can I use a column alias defined in SELECT inside PARTITION BY or ORDER BY of a window function in the same query?
Yes — ORDER BY and PARTITION BY inside OVER (...) are evaluated after the SELECT list, so column aliases defined in SELECT are visible there. This is one of the few places aliases are usable in the same query level. The restriction applies to WHERE and HAVING, not to window clause definitions.
My query works in MySQL but raises 42P10 in PostgreSQL — why?
MySQL has more permissive scoping rules for correlated subqueries and window functions. PostgreSQL enforces stricter SQL-standard scoping, which is why a query that MySQL accepts can be rejected by PostgreSQL. The fix is typically to rewrite using a LATERAL join or a CTE so that the correlated column is properly in scope.
Does 42P10 ever appear at runtime rather than at query parse time?
No. 42P10 is always detected during parsing or planning, before execution begins. If you are using a framework that defers preparation (e.g., some ORMs that build queries dynamically), you might see the error appear to coincide with a specific runtime code path — but that is because the query string was only sent to PostgreSQL at that point, not because the error itself is a runtime condition.