34000 — invalid_cursor_name
34000 — invalid_cursor_name
At a glance
34000 means that a cursor or portal name cannot be resolved in the current backend session. A cursor that was closed, declared on another pooled connection, or ended with its transaction is not the same resource as a cursor with a bad position (24000).
| Field | Value |
|---|---|
| SQLSTATE | 34000 |
| Condition | invalid_cursor_name |
| Status | active |
| Known present by | 7.4 |
| Locked snapshots | 9.0.23, 9.1.24, 9.2.24, 9.3.25, 9.4.26, 9.5.25, 9.6.24, 10.23, 11.22, 12.22, 13.23, 14.24, 15.19, 16.15, 17.11, 18.6, 19beta3 |
| Macros | ERRCODE_INVALID_CURSOR_NAME, ERRCODE_UNDEFINED_CURSOR |
| Aliases | ERRCODE_UNDEFINED_CURSOR |
Meaning
The executor’s portal path reports cursor "%s" does not exist when FETCH cannot find the named portal. Cursor names are tied to a backend session, and ordinary cursors are tied to a transaction unless declared with an appropriate hold option. A pool checkout therefore must keep declaration and use on the same connection and within the intended transaction boundary.
Diagnosis
Record SQLSTATE, the dynamic cursor name, backend PID, and transaction status. Check the application path for an earlier CLOSE, an implicit commit, a connection return to the pool, or a different session. pg_cursors and the server-side session identity can help when the diagnosis runs on the same backend; a new connection cannot inspect or fetch a cursor owned by the old one.
Response
For an explicit block, rollback after the failed FETCH before issuing more commands. Re-declare the cursor on the same session, or deliberately redesign the workflow to materialize keys/results when work must cross pool checkouts. Use a holdable cursor only when its commit-survival semantics are intended; it does not make a cursor portable between sessions.
Messages
The selected core path uses cursor "%s" does not exist; protocol and PL/pgSQL paths can use portal "%s" or the same cursor wording. The name is dynamic, so use the SQLSTATE and diagnostic fields rather than a full-message equality check across versions and callers.
Representative case
The shared registry in verify/cases/34000/snippets.json (SHA-256 ad5b519e8ca30fd2636b1d0bace32dc18558cccc7b9d360081c27b12a1405f08) declares and closes a cursor, proves the missing-FETCH error, rolls back, and recreates it in the same session. See the public case export and structured evidence.
The runner substitutes a unique cursor name. The first FETCH is intentionally after CLOSE; the second FETCH occurs after a new BEGIN and declaration, so the successful row proves lifecycle repair rather than a client-side mock.
The selected case observed 34000 on PostgreSQL 18.6 and 10.21 after closing a named cursor and attempting FETCH. The failed block reached INERROR; rollback followed by a new declaration, fetch, close, and commit returned the session to IDLE.
Versions
The locked catalogue records this condition across the formal snapshots. 18.6 and 10.23 source scans both contain the portal and PL/pgSQL paths; the selected runtime case observes the same SQLSTATE on 18.6 and 10.21, while exact source line and caller wording vary.
Related
Compare 24000 invalid cursor state for a cursor that exists but is not positioned, and 26000 invalid prepared statement name for another session-local resource.
Sources
src.errcodes.18.6(SHA-2566e8de346643ba84aa3c9c6a73360acfc7b2dfb89162c06c08ce9bf5bcd5bbcba)src.portalcmds.18.6(SHA-256e71c5bdb2da67771fb5f42f18b823e8af3ee6c31d5314e97a47ad18f7788bf35)DECLARE documentation· local call scansrc.calls.REL_18_6(SHA-2569ee8a0e81d8f0825c5c1ae45583439859a26e602bdd4ce2f2a62aa278867ccbf)