Incident brief
Cursor does not exist
A CLOSE (or FETCH/MOVE) named a cursor that was never declared, or was already closed. PostgreSQL has no OPEN statement, a cursor exists only once you DECLARE it.
What lands in your log
ERROR: cursor "order_cursor" does not exist
In 10 seconds
- What triggers it
- Begin a transaction.
- Fix
- DECLARE the cursor before you FETCH, MOVE, or CLOSE it, there is no implicit OPEN.
- Proof
- Reproduced on PostgreSQL 16.14 → CLOSE of an undeclared cursor was rejected with SQLSTATE 34000, aborting the transaction. Declaring the cursor first let FETCH and CLOSE succeed.
Fix
What to do right now
Application-level steps for this error.
- DECLARE the cursor before you FETCH, MOVE, or CLOSE it, there is no implicit OPEN.
- Remember non-holdable cursors are implicitly closed at COMMIT/ROLLBACK; don't reuse the name across transactions.
- Query pg_cursors to see which cursors are actually open in the current session.
-- declare the cursor before you FETCH or CLOSE it
BEGIN;
DECLARE order_cursor CURSOR FOR SELECT g FROM generate_series(1, 3) AS g;
FETCH ALL FROM order_cursor;
CLOSE order_cursor;
COMMIT;For this error
See this error live on the server
Run these against the affected instance to confirm the diagnosis before you act.
Cursor does not exist is connection-local. Inspect cursors on the same pooled connection; another session cannot see them.
Open cursors in this session
Confirm cursor name, holdability, and transaction scope.
SELECT name, statement, is_holdable, is_binary, is_scrollable, creation_time
FROM pg_cursors
ORDER BY creation_time;Why it happens
What PostgreSQL is telling you
The mechanism behind the error, grounded in the official manual, not paraphrased.
PostgreSQL 16 Documentation, SQL Commands: CLOSE
PostgreSQL does not have an explicit OPEN cursor statement; a cursor is considered open when it is declared. Use the DECLARE statement to declare a cursor.Read the full section on postgresql.org →
Closing a cursor that was never declared
There is no cursor named order_cursor in this session, nothing declared it, so CLOSE raises SQLSTATE 34000, and the transaction switches to the aborted state.Recovering the aborted transaction
The transaction was aborted by the error, so it must be rolled back before the session accepts new work; after ROLLBACK the next statement runs normally.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.
- 1Begin a transaction.
- 2CLOSE a cursor name that was never declared.
- 3PostgreSQL raises SQLSTATE 34000 and the transaction is now in the aborted state.
One client: inside a transaction, CLOSE names a cursor that was never declared.
-- The cursor error is self-contained inside one transaction; no tables required.
SELECT 'no schema needed' AS setup_note;BEGIN;
CLOSE order_cursor;ROLLBACK;
SELECT 'ok' AS session_after_rollback;What PostgreSQL actually returned
setup_note
------------------
no schema needed
(1 row)BEGIN
ERROR: cursor "order_cursor" does not existROLLBACK
session_after_rollback
------------------------
ok
(1 row)The full DECLARE → FETCH → CLOSE sequence was run inside one transaction and returned the rows and clean command tags, proving the cursor only needed to be declared first.
Without this
Above: CLOSE without a DECLARE raises SQLSTATE 34000.
With this, tested
Below: DECLARE first, and FETCH/CLOSE both succeed.
- A second operational test: exact SQL, raw output, measured result, and engineer notes
Card required. Cancel before day 7 and you are not charged.
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.
Verification
- Last verified
- 2026-07-19 (isolated lab, PostgreSQL 16.14)
- Verification scope
- Verified against PostgreSQL 16.14 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.