PostgreSQL raises error 22013 when a window function frame specification uses an invalid value for PRECEDING or FOLLOWING row offsets. The frame size must be a non-negative integer expression, evaluated at runtime, and cannot be negative or NULL. This error occurs during query execution when the computed offset violates these constraints.
The "22013: invalid_preceding_or_following_size" SQLSTATE belongs to class 22 (Data Exception) and specifically validates the numeric offset values used in window frame clauses like ROWS BETWEEN n PRECEDING AND m FOLLOWING. Unlike syntax errors that fail at parse time, this error is raised during execution when PostgreSQL evaluates the frame offset expression and finds it produces a negative number, NULL, or otherwise invalid result. The window executor in nodeWindowAgg.c checks these bounds before constructing the frame. Valid offsets must be non-negative integers; zero is permitted and selects the current row itself.
Check the query's OVER clause for any ROWS BETWEEN, RANGE BETWEEN, or GROUPS BETWEEN specifications. Note both the PRECEDING and FOLLOWING values. Use psql with \set VERBOSITY verbose to see the full error context and position.
-- Example that triggers 22013
SELECT
order_id,
AVG(amount) OVER (
ORDER BY order_date
ROWS BETWEEN -1 PRECEDING AND CURRENT ROW
) AS moving_avg
FROM orders;
-- ERROR: 22013: invalid_preceding_or_following_sizeEnsure all frame offsets are positive integers or zero. If using expressions, validate they cannot produce negative results. Replace problematic calculations with GREATEST(expr, 0) or COALESCE(expr, 0) to enforce non-negative bounds.
-- INVALID: negative offset
SELECT
product_id,
SUM(sales) OVER (
ORDER BY sale_date
ROWS BETWEEN -7 PRECEDING AND CURRENT ROW
)
FROM sales;
-- VALID: use positive offset
SELECT
product_id,
SUM(sales) OVER (
ORDER BY sale_date
ROWS BETWEEN 7 PRECEDING AND CURRENT ROW
)
FROM sales;If the frame size is computed dynamically from table columns or parameters, add validation to ensure non-negative results. Use CASE statements or GREATEST() to prevent negative values.
-- INVALID: dynamic offset from column that might be negative
SELECT
user_id,
AVG(score) OVER (
ORDER BY game_date
ROWS BETWEEN window_size PRECEDING AND CURRENT ROW
)
FROM game_scores;
-- VALID: ensure offset is non-negative
SELECT
user_id,
AVG(score) OVER (
ORDER BY game_date
ROWS BETWEEN GREATEST(window_size, 0) PRECEDING AND CURRENT ROW
)
FROM game_scores
WHERE window_size IS NOT NULL;Frame offsets cannot be NULL. If your offset expression might produce NULL, use COALESCE to provide a default non-negative value or filter out NULL cases before the window function.
-- INVALID: offset might be NULL
SELECT
transaction_id,
SUM(amount) OVER (
ORDER BY tx_date
ROWS BETWEEN days_back PRECEDING AND CURRENT ROW
)
FROM transactions;
-- VALID: provide default for NULL
SELECT
transaction_id,
SUM(amount) OVER (
ORDER BY tx_date
ROWS BETWEEN COALESCE(days_back, 0) PRECEDING AND CURRENT ROW
)
FROM transactions;After fixing the offset expression, test with actual data that previously failed. Verify the query handles minimum values, NULL values, and boundary conditions correctly. Check execution plans to ensure offsets are computed as expected.
-- Test with various offset values
SELECT
id, value,
AVG(value) OVER (
ORDER BY id
ROWS BETWEEN GREATEST(offset_col, 0) PRECEDING AND CURRENT ROW
) AS moving_avg
FROM test_data
WHERE offset_col IS NOT NULL
ORDER BY id
LIMIT 10;Error 22013 is raised by the window function executor (nodeWindowAgg.c) during runtime frame construction, distinguishing it from 42P20 windowing_error which catches syntax violations at parse time. The executor evaluates frame offset expressions once per row when building the window frame, so this error can occur inconsistently across a result set if the offset expression depends on row data. PostgreSQL enforces that frame offsets must be integer expressions and cannot contain variables, aggregate functions, or window functions themselves—these restrictions are validated earlier—but the non-negative constraint is checked at execution time. When migrating from other databases that allow negative offsets or interpret them differently, ensure your window specifications use only non-negative integers. For dynamic window sizes, consider using CTEs to pre-compute and validate offset values before passing them to window functions.
ERROR: syntax error at end of input
Syntax error at end of input in PostgreSQL
Bind message supplies N parameters but prepared statement requires M
Bind message supplies N parameters but prepared statement requires M in PostgreSQL
Multidimensional arrays must have sub-arrays with matching dimensions
Multidimensional arrays must have sub-arrays with matching dimensions
ERROR: value too long for type character varying
Value too long for type character varying
insufficient columns in unique constraint for partition key
How to fix "insufficient columns in unique constraint for partition key" in PostgreSQL