Skip to content

42P03 — Duplicate cursor

PostgreSQL SQLSTATE 42P03: Duplicate cursor (duplicate_cursor), source-backed diagnosis and recovery guidance.

42P03 — Duplicate cursor

At a glance

42P03 (duplicate_cursor) reports a cursor/portal name collision, but the same SQLSTATE covers an ERROR, a replacement WARNING, and a PL/pgSQL-specific active-cursor ERROR. The caller and the CreatePortal options determine which behavior occurred.

Field Value
SQLSTATE 42P03
Condition duplicate_cursor
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_DUPLICATE_CURSOR
Aliases

Meaning

CreatePortal(name, allowDup, dupSilent) raises ERROR cursor "%s" already exists when allowDup is false. When allowDup is true, it can emit WARNING closing existing cursor "%s" unless dupSilent is set, then drops the old portal and creates the new one. PL/pgSQL takes a different branch: SPI_cursor_find detects an already active named cursor and raises ERROR cursor "%s" already in use.

Diagnosis

Record the exact name, source (SQL portal creation or PL/pgSQL OPEN), severity, and transaction state. For SQL, inspect portal creation/reuse and pg_cursors where that view exposes the portal; for PL/pgSQL, inspect the cursor variable and the OPEN/CLOSE flow. The allowDup replacement branch is not evidence that a user-level duplicate declaration always warns, and the PL/pgSQL branch is not the portal-manager replacement path.

Response

For an ERROR from a non-replacing portal path, close the old portal or choose a name whose transaction lifetime is correct. A WARNING replacement path already closes the old portal, so the application must verify that replacement is intended rather than treating the warning as a failed close. For PL/pgSQL, close the named cursor before reopening it or use a distinct cursor variable/name. Only ERROR paths require explicit-transaction recovery with a savepoint rollback or transaction rollback; reconnecting can hide pool ownership problems.

Messages

Representative 18.6 messages are:

Path Severity Primary
CreatePortal(..., allowDup=false, ...) ERROR cursor "%s" already exists
CreatePortal(..., allowDup=true, dupSilent=false) WARNING closing existing cursor "%s"
PL/pgSQL named cursor already found ERROR cursor "%s" already in use

The %s value is dynamic; no concrete cursor name is claimed.

Versions

From 7.4 through 18.6 and 19beta3; the catalogue range is not a precise introduction claim. The selected portal-manager and PL/pgSQL branches are fixed 18.6 source paths, and runtime was not run.

Sources

  • src.errcodes.42P03.18.6src/backend/utils/errcodes.txt line 378, fixed at 724edf9bde9d356724ad384a2e196edc3c9f80f7; SHA-256 6e8de346643ba84aa3c9c6a73360acfc7b2dfb89162c06c08ce9bf5bcd5bbcba (source).

  • src.call.42P03.800a8278cf453417f5af6d6esrc/backend/utils/mmgr/portalmem.c lines 185-187, fixed at 724edf9bde9d356724ad384a2e196edc3c9f80f7; SHA-256 8e29a302a7837316765acc798f9ff6eb22a330e8098a02794cd4d683601d3b04 (source).

  • src.call.42P03.208bdd3b576b5021fb35916dsrc/backend/utils/mmgr/portalmem.c lines 189-192, fixed at 724edf9bde9d356724ad384a2e196edc3c9f80f7; SHA-256 8e29a302a7837316765acc798f9ff6eb22a330e8098a02794cd4d683601d3b04 (source).

  • src.call.42P03.06c0580b43488eece2de1973src/pl/plpgsql/src/pl_exec.c lines 2895-2897, fixed at 724edf9bde9d356724ad384a2e196edc3c9f80f7; SHA-256 0df3c70a6bccf6dddf443fb99e152a16a67009827c44a820597e98e585cb8b20 (source).

  • src.calls.REL_18_6.42P03 — resolved core call groups; SHA-256 9ee8a0e81d8f0825c5c1ae45583439859a26e602bdd4ce2f2a62aa278867ccbf.

  • authored evidence — source claims, message roles, and runtime boundary.