Incident brief
Negative OFFSET in a query
The OFFSET clause must skip a non-negative number of rows; an OFFSET that evaluates to a negative value raises this data exception.
What lands in your log
ERROR: OFFSET must not be negative
In 10 seconds
- What triggers it
- Run a SELECT whose OFFSET evaluates to a negative number.
- Fix
- Use an OFFSET of 0 or greater.
- Proof
- Reproduced on PostgreSQL 18.4 → A single query reproduces SQLSTATE 2201X; the planner rejects the negative OFFSET before returning any rows.
Fix
What to do right now
Application-level steps for this error.
- Use an OFFSET of 0 or greater.
- Clamp a computed offset with GREATEST(offset, 0), especially when it comes from pagination arithmetic like (page - 1) * size.
-- OFFSET must be zero or positive.
SELECT g FROM generate_series(1,3) g OFFSET 0;For this error
See this error live on the server
Run these against the affected instance to confirm the diagnosis before you act.
Negative OFFSET in a query (2201X) is statement-local: PostgreSQL does not retain the rejected value after the statement ends. Capture the exact bound value and statement position in application/server logs, then run this targeted validation before retrying.
Validate the bound OFFSET
Validate the specific value/shape that can raise this SQLSTATE.
SELECT requested_offset,
requested_offset < 0 AS would_raise_2201X
FROM (VALUES (-1)) v(requested_offset);Why it happens
What PostgreSQL is telling you
The mechanism behind the error, grounded in the official manual, not paraphrased.
PostgreSQL 18 Documentation, Appendix A. PostgreSQL Error Codes (Table A.1, Class 22, Data Exception)
2201X → invalid_row_count_in_result_offset_clauseRead the full section on postgresql.org →
Supplying a negative OFFSET
OFFSET counts rows to skip and cannot be negative, so PostgreSQL raises 'OFFSET must not be negative'.The session continues normally
The OFFSET check failed before any transaction was opened, so there's nothing for session B to undo before its next statement.Reproduce & verify
A real, single-session PostgreSQL reproduction
A literal transcript of SQL run against a live PostgreSQL instance in an isolated lab. The commands below are exactly what was executed.
- 1Run a SELECT whose OFFSET evaluates to a negative number.
- 2PostgreSQL checks the row-count argument of OFFSET before fetching rows.
- 3A negative value aborts the statement with SQLSTATE 2201X.
A paginator computed OFFSET as (page - 1) * page_size and produced a negative offset for page 0, aborting the listing query.
-- The error is self-contained in one statement; no schema is required.
SELECT 'no schema needed' AS setup_note;SELECT g FROM generate_series(1,3) g OFFSET -1;SELECT 'ok' AS session_after_error;What PostgreSQL actually returned
setup_note
------------------
no schema needed
(1 row)ERROR: OFFSET must not be negative session_after_error
---------------------
ok
(1 row)The same query returns rows once the OFFSET is non-negative.
Without this
Before: a negative OFFSET aborts
With this, tested
After: OFFSET 0 returns the rows
- A second operational test: exact SQL, raw output, measured result, and engineer notes
Card required. Cancel before day 7 and you are not charged.
Runbook to fix this
The runbook for this incident
Full step-by-step fixes for the condition behind this error: the diagnosis, the exact SQL, and output captured in the lab.
Connected
Everything this error touches
Every page this SQLSTATE connects to: the concept that explains it, the runbooks that fix it, the parameters you tune to prevent it, and the sibling errors it travels with. All real cross-references. Jump straight in, or open the full interactive map.
Fix it — runbooks
Related errors
Verification
- Last verified
- 2026-07-24 (isolated lab, PostgreSQL 18.4)
- Verification scope
- Verified against PostgreSQL 18.4 in an isolated lab environment
- Audit status
- reviewed
Went further?
Pro unlocks the second lab proof
Free page stops the bleeding. Pro adds the operational test, SQLSTATE audit, and deeper evidence, same error, more certainty.