When PostgreSQL raises an error in the 54 — Program Limit Exceeded class, you will see a message like ERROR: stack depth limit exceeded or ERROR: too many columns along with SQLSTATE 54000 (or one of its subclass codes such as 54001 or 54011). The condition name is program_limit_exceeded. These errors signal that a query, function, or object definition has hit a hard internal limit built into the PostgreSQL server.
What This Error Means
SQLSTATE class 54 groups errors that occur when PostgreSQL's own internal resource or complexity limits are breached. Unlike resource errors in class 53 (which relate to system resources like memory and disk), class 54 errors are about structural limits baked into the PostgreSQL source — maximum column counts, parse-tree depth, and recursion depth in the executor or planner.
SQLSTATE 54000 itself is the generic parent of this class. In practice, PostgreSQL almost always raises a more specific subclass code rather than the bare 54000. The two most commonly encountered subclass codes are 54001 (statement_too_complex) and 54011 (too_many_columns). When a driver or client library reports 54000, it usually means the server sent the generic parent — this can happen in older PostgreSQL versions or via certain PL/pgSQL paths that do not map the condition to a subclass.
After any 54xxx error, the current transaction is aborted. No data modification from the failed statement is preserved, and you must issue a ROLLBACK (or ROLLBACK TO SAVEPOINT) before executing further statements in the session.
Common Causes
Recursive function or query with unbounded depth (
54001/ stack depth limit exceeded). A PL/pgSQL function or a recursive SQL expression (WITH RECURSIVE) recurses too deeply, exhausting the stack. PostgreSQL enforces amax_stack_depthlimit (default 2 MB, derived from the OS stack size) to avoid a hard process crash.Table or query result with too many columns (
54011). PostgreSQL supports a maximum of 1,600 columns per table (and a lower practical limit that varies with column types due to tuple storage constraints). Attempting to create a table exceeding this limit, or generating a result set with more columns via a wideSELECT *join, raises this error.Extremely complex query plan or expression tree (
54001). Queries with deeply nested subqueries, many levels of CTEs, or very largeIN (...)lists can produce a parse or plan tree deep enough to overflow the stack during planning or execution.Large number of range table entries. Joining a very large number of tables in a single query can exceed PostgreSQL's internal limit on range table entries, which is tied to the same stack-depth mechanism.
Bare
54000from a custom PL/pgSQLRAISE. Application code or extensions occasionally raiseprogram_limit_exceededdirectly usingRAISE SQLSTATE '54000'when enforcing application-level structural limits, though this is uncommon.
How to Fix program_limit_exceeded
For stack depth / recursion errors — increase
max_stack_depth(carefully). Check your OS hard stack limit first, then raise the PostgreSQL parameter to slightly below it:# Check OS limit (Linux/macOS) ulimit -s-- View current PostgreSQL setting SHOW max_stack_depth; -- Raise it in postgresql.conf or at session level (value in kB) -- Only raise to ~1 MB below the OS ulimit -s value SET max_stack_depth = '4MB';A better long-term fix is to rewrite recursive logic iteratively or add a recursion depth guard:
-- Add a depth counter to your recursive CTE WITH RECURSIVE tree AS ( SELECT id, parent_id, 1 AS depth FROM nodes WHERE parent_id IS NULL UNION ALL SELECT n.id, n.parent_id, t.depth + 1 FROM nodes n JOIN tree t ON n.parent_id = t.id WHERE t.depth < 100 -- hard recursion limit ) SELECT * FROM tree;For too-many-columns errors — normalize your schema. If you are hitting the 1,600-column ceiling, the schema needs normalization. Store variable attributes in a
jsonbcolumn or a separate entity-attribute-value (EAV) table rather than as individual columns:-- Instead of 500 separate attribute columns: ALTER TABLE products ADD COLUMN attributes jsonb; -- Query a specific attribute with a GIN index CREATE INDEX ON products USING GIN (attributes); SELECT * FROM products WHERE attributes @> '{"color": "red"}';For overly complex queries — break them up. Refactor deeply nested subqueries into a series of CTEs or temporary tables, each processed as its own statement:
-- Materialize an intermediate result to break query complexity CREATE TEMP TABLE mid_result AS SELECT ... FROM large_nested_subquery; SELECT * FROM mid_result JOIN other_table ...;For large
INlists — use= ANY(ARRAY[...])or a join. Very largeINclauses build deep expression trees. Replace them with an array predicate or a temporary table:-- Prefer this SELECT * FROM orders WHERE id = ANY(ARRAY[1,2,3,...,10000]); -- Or use a VALUES join SELECT o.* FROM orders o JOIN (VALUES (1),(2),(3),...) AS ids(id) ON o.id = ids.id;
Additional Information
- SQLSTATE
54000(program_limit_exceeded) is the parent class; the two defined subclasses are54001(statement_too_complex) and54011(too_many_columns). Most PostgreSQL errors in this class use the subclass codes. max_stack_depthdefaults to 2 MB. The PostgreSQL documentation recommends setting it to at most 1 MB less than the OS-level stack size limit (ulimit -s).- In PostgreSQL 8.0 and later, stack depth checking was formalized using
check_stack_depth()internally, so the54001error is reliably raised before an actual segfault. - Most application drivers (libpq, psycopg2, JDBC, node-postgres) surface this as a generic database exception with the SQLSTATE code included. In psycopg2 it raises
psycopg2.errors.ProgramLimitExceeded; in JDBC it appears as aPSQLExceptionwith SQLState54000/54001/54011. - ORM-generated queries that join many associations in a single call (e.g., ActiveRecord
includeswith deeply nested associations) can trigger stack-depth errors under load. The fix is usually to split eager loads across separate queries.
Frequently Asked Questions
Why does PostgreSQL have a column limit at all?
PostgreSQL stores each row as a heap tuple with a fixed-size header and a bitmap for null values. The physical tuple format was designed with a 1,600-column ceiling for practical storage reasons; very wide rows also degrade performance significantly because each row scan must process all column offsets. For truly wide data, jsonb or a separate child table is the idiomatic PostgreSQL solution.
What is the difference between SQLSTATE 54000 and 54001?
54000 is the generic parent of the "program limit exceeded" class and rarely appears on its own. 54001 (statement_too_complex) is raised specifically when stack depth is exceeded during query planning or execution (including unbounded recursion). Think of 54000 as the catch-all and 54001 as the most common concrete trigger.
Can I increase the recursion limit without changing max_stack_depth?
Yes — add an explicit depth counter to your WITH RECURSIVE query (as shown above) or in your PL/pgSQL function. This is generally safer than raising max_stack_depth because it prevents runaway recursion from ever reaching the stack limit, and it makes the depth constraint explicit and auditable.
My query worked fine on PostgreSQL 12 but fails with 54001 on PostgreSQL 15. Why?
The query planner has grown more sophisticated across versions, and deeply nested queries may now be planned with a different (sometimes deeper) execution path. Additionally, new planner features like parallel query or partition pruning can increase internal recursion depth. Refactoring the query to use explicit CTEs (WITH ... AS MATERIALIZED) to force materialization boundaries is usually the most reliable fix.