PostgreSQL Error 2201W: Invalid Row Count in LIMIT Clause
PostgreSQL error code 2201W (invalid_row_count_in_limit_clause) is thrown when the value provided to a LIMIT clause is not a valid non-negative integer. Specifically, passing a negative number, a non-integer expression that resolves to an invalid value, or an improperly typed variable will trigger this error at runtime. It commonly surfaces in applications that dynamically construct SQL queries or bind user-supplied pagination parameters directly to queries.
Top 3 Causes
1. Passing a Negative Value Directly to LIMIT
The most frequent cause is a miscalculated pagination value that results in a negative integer being bound to the LIMIT clause.
-- This will raise ERROR 2201W
SELECT * FROM orders LIMIT -10;
-- Also fails when a variable holds a negative value
DO $$
DECLARE
v_limit INT := -5;
BEGIN
-- Runtime error: invalid row count in limit clause
EXECUTE 'SELECT * FROM orders LIMIT ' || v_limit;
END;
$$;
2. Unvalidated Function or Subquery Results Used in LIMIT
When a subquery or function is used to compute the LIMIT value, unexpected data conditions can produce invalid results.
-- Dangerous pattern: subquery may return a negative or NULL value
SELECT *
FROM products
LIMIT (SELECT total_stock - reserved_stock FROM inventory WHERE id = 1);
-- If reserved_stock > total_stock, result is negative → 2201W!
-- Also risky: unguarded CASE expression
SELECT *
FROM events
LIMIT (
CASE
WHEN category = 'A' THEN 50
-- Missing ELSE can cause NULL in some PostgreSQL contexts
END
);
3. Uninitialized or Miscalculated Variables in PL/pgSQL
Inside stored procedures and PL/pgSQL blocks, variables used for LIMIT can carry unexpected values due to logic errors or missing initialization.
-- Problematic function
CREATE OR REPLACE FUNCTION fetch_logs(p_multiplier INT)
RETURNS SETOF logs AS $$
DECLARE
v_limit INT;
BEGIN
-- Bug: if p_multiplier is negative, v_limit becomes negative
v_limit := 100 * p_multiplier;
RETURN QUERY SELECT * FROM logs LIMIT v_limit; -- 2201W if negative!
END;
$$ LANGUAGE plpgsql;
Quick Fix Solutions
Use GREATEST() and COALESCE() as your first line of defense to sanitize LIMIT values before they reach the query engine.
-- Fix 1: Use GREATEST to guarantee a non-negative floor
SELECT * FROM orders
LIMIT GREATEST(0, :user_supplied_limit);
-- Fix 2: COALESCE + GREATEST for NULL safety
SELECT * FROM products
LIMIT COALESCE(GREATEST(0, :limit_param), 100);
-- Fix 3: Safe PL/pgSQL function with input validation
CREATE OR REPLACE FUNCTION fetch_logs_safe(p_multiplier INT)
RETURNS SETOF logs AS $$
DECLARE
v_limit INT;
BEGIN
v_limit := GREATEST(1, LEAST(100 * COALESCE(p_multiplier, 1), 10000));
RETURN QUERY SELECT * FROM logs ORDER BY created_at DESC LIMIT v_limit;
END;
$$ LANGUAGE plpgsql;
-- Fix 4: Safe dynamic SQL execution
DO $$
DECLARE
v_limit INT := GREATEST(0, -5); -- Corrects to 0
BEGIN
EXECUTE format('SELECT * FROM orders LIMIT %s', v_limit);
END;
$$;
Prevention Tips
Validate at every layer. Never trust a raw user input or unchecked computed value as a LIMIT argument. Apply input validation in your application code first, then add a GREATEST(0, ...) guard in SQL as a second layer of defense. Set both a minimum (0) and a maximum (e.g., 10,000) to prevent runaway queries.
-- Use a domain type to enforce constraints at the DB level
CREATE DOMAIN safe_limit AS INT
CHECK (VALUE >= 0 AND VALUE <= 10000);
-- Pair LIMIT with OFFSET defensively
SELECT * FROM users
ORDER BY id
LIMIT GREATEST(0, COALESCE(:page_size, 20))
OFFSET GREATEST(0, COALESCE(:offset_val, 0));
Use CHECK constraints on config tables that store pagination settings, and lint your PL/pgSQL functions with thorough unit tests covering edge cases like p_limit = 0, p_limit = -1, and p_limit = NULL.
Related Errors
| Error Code | Name | Description |
|---|---|---|
| 2201X | invalid_row_count_in_result_offset_clause |
Same class of error but for the OFFSET clause; always fix both together. |
| 22003 | numeric_value_out_of_range |
Fires when the LIMIT value exceeds the integer range. |
| 22P02 | invalid_text_representation |
Occurs when a non-numeric string is cast to integer for use in LIMIT. |
📖 Want a more detailed guide?
Check out the full in-depth version (Korean) on oraerror.com — includes detailed analysis, additional SQL examples, and prevention tips.
Top comments (0)