select open change scope Open full search

PG.CENTER connects PostgreSQL documentation, reference, and ecosystem knowledge. Maintained by Pigsty.

SQLSTATE / CLASS 34 · INVALID CURSOR NAME

invalid_cursor_name

SQLSTATE
34000
Condition name
invalid_cursor_name
Class
Invalid Cursor Name
Source macro
ERRCODE_INVALID_CURSOR_NAME
Evidence
Observed at runtime in the source evidence
Analysis & operational context

English SQLSTATE atlas: authored explanations and source/runtime evidence are separate from the manual definitions. View source ↗

<h1>34000 — invalid_cursor_name</h1>

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).

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.

BEGIN;
DECLARE cursor_name CURSOR FOR SELECT 1;
CLOSE cursor_name;
FETCH cursor_name;
ROLLBACK;
BEGIN;
DECLARE cursor_name CURSOR FOR SELECT 1;
FETCH cursor_name;
CLOSE cursor_name;
COMMIT;

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.

Sources

  • src.errcodes.18.6 (SHA-256 6e8de346643ba84aa3c9c6a73360acfc7b2dfb89162c06c08ce9bf5bcd5bbcba)
  • src.portalcmds.18.6 (SHA-256 e71c5bdb2da67771fb5f42f18b823e8af3ee6c31d5314e97a47ad18f7788bf35)
  • DECLARE documentation · local call scan src.calls.REL_18_6 (SHA-256 9ee8a0e81d8f0825c5c1ae45583439859a26e602bdd4ce2f2a62aa278867ccbf)

Source evidence

Evidence belongs to the frozen source and runtime versions listed here. It is not a runtime verification of the selected manual version.

34000 is the invalid_cursor_name condition in Class 34 and shares the undefined-cursor alias.

Method: Read the fixed errcodes.txt row and macro mapping.

Limits: The same SQLSTATE covers several cursor/portal-facing paths.

src.errcodes.18.6

PerformPortalFetch emits cursor "%s" does not exist when the named portal is absent.

Method: Trace ERRCODE_UNDEFINED_CURSOR in portalcmds.c.

Limits: Other callers use portal or cursor wording; the name is dynamic.

src.portalcmds.18.6 src.calls.REL_18_6

A named cursor belongs to its backend session and transaction lifecycle; closing it or losing its transaction makes a later FETCH invalid.

Method: Compare DECLARE cursor lifecycle with the explicit same-session registry case.

Limits: Holdable cursors and client-side cursor abstractions have additional rules.

doc.declare.18.6

The selected 34000 case passed on PostgreSQL 18.6 and 10.21 with the structured SQLSTATE, transaction-state, and repair assertions recorded in the runtime entries.

Method: Read the selected runner summaries and raw outputs tied to the shared snippet registry.

Limits: This covers the selected mechanism only, not all source callers or all driver/pool behavior.

runtime.34000-batch34-20260909.latest runtime.34000-batch34-20260909.pg10

Message templates

ERROR · message.cursor-missing

Primary

cursor "%s" does not exist

The portal protocol path uses portal "%s" does not exist.

Reproduction & repair cases

cursor_lifecycle_recovery · PG 10, 18

Preconditions

  • A runner-owned disposable target is provisioned.

Trigger: Declare and close a named cursor, attempt to use it again, then recreate it in a new explicit transaction on the same session.

Expected assertions

  • SQLSTATE is 34000
  • The missing cursor diagnostic is preserved
  • The failed block recovers through ROLLBACK
  • A recreated cursor can FETCH and COMMIT

Repair: Keep cursor use on the declaring backend session and respect its transaction lifetime; after an error, roll back before recreating it.

Cleanup: Drop the case schema with an owner connection.

Recorded runtime evidence

18.6 (Homebrew) · passed

Run: 34000-batch34-20260909

{
  "repair": {
    "fetched_row": [
      1
    ]
  },
  "primary": "cursor \"case_cursor_aa951a82\" does not exist",
  "severity": "ERROR",
  "sqlstate": "34000",
  "statuses": {
    "after_error": "INERROR",
    "after_repair": "IDLE",
    "after_rollback": "IDLE"
  },
  "connection_autocommit": true
}
10.21 (Debian 10.21-1.pgdg90+1) · passed

Run: 34000-batch34-20260909

{
  "repair": {
    "fetched_row": [
      1
    ]
  },
  "primary": "cursor \"case_cursor_d0bb43e5\" does not exist",
  "severity": "ERROR",
  "sqlstate": "34000",
  "statuses": {
    "after_error": "INERROR",
    "after_repair": "IDLE",
    "after_rollback": "IDLE"
  },
  "connection_autocommit": true
}

Definition snapshot: english-manuals:f42d4c9463d45d30ea3dc66d08b… · English manual source