PostgreSQL Program Limit Exceeded (SQLSTATE 54000)

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

  1. 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 a max_stack_depth limit (default 2 MB, derived from the OS stack size) to avoid a hard process crash.

  2. 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 wide SELECT * join, raises this error.

  3. Extremely complex query plan or expression tree (54001). Queries with deeply nested subqueries, many levels of CTEs, or very large IN (...) lists can produce a parse or plan tree deep enough to overflow the stack during planning or execution.

  4. 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.

  5. Bare 54000 from a custom PL/pgSQL RAISE. Application code or extensions occasionally raise program_limit_exceeded directly using RAISE SQLSTATE '54000' when enforcing application-level structural limits, though this is uncommon.

How to Fix program_limit_exceeded

  1. 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;
    
  2. 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 jsonb column 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"}';
    
  3. 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 ...;
    
  4. For large IN lists — use = ANY(ARRAY[...]) or a join. Very large IN clauses 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 are 54001 (statement_too_complex) and 54011 (too_many_columns). Most PostgreSQL errors in this class use the subclass codes.
  • max_stack_depth defaults 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 the 54001 error 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 a PSQLException with SQLState 54000/54001/54011.
  • ORM-generated queries that join many associations in a single call (e.g., ActiveRecord includes with 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.

Subscribe to the Pulse Newsletter

Get early access to new Pulse features, insightful blogs & exclusive events , webinars, and workshops.

We use cookies to provide an optimized user experience and understand our traffic. To learn more, read our use of cookies; otherwise, please choose 'Accept Cookies' to continue using our website.