42P03 — Duplicate cursor
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.
Related
Sources
-
src.errcodes.42P03.18.6—src/backend/utils/errcodes.txtline 378, fixed at724edf9bde9d356724ad384a2e196edc3c9f80f7; SHA-2566e8de346643ba84aa3c9c6a73360acfc7b2dfb89162c06c08ce9bf5bcd5bbcba(source). -
src.call.42P03.800a8278cf453417f5af6d6e—src/backend/utils/mmgr/portalmem.clines 185-187, fixed at724edf9bde9d356724ad384a2e196edc3c9f80f7; SHA-2568e29a302a7837316765acc798f9ff6eb22a330e8098a02794cd4d683601d3b04(source). -
src.call.42P03.208bdd3b576b5021fb35916d—src/backend/utils/mmgr/portalmem.clines 189-192, fixed at724edf9bde9d356724ad384a2e196edc3c9f80f7; SHA-2568e29a302a7837316765acc798f9ff6eb22a330e8098a02794cd4d683601d3b04(source). -
src.call.42P03.06c0580b43488eece2de1973—src/pl/plpgsql/src/pl_exec.clines 2895-2897, fixed at724edf9bde9d356724ad384a2e196edc3c9f80f7; SHA-2560df3c70a6bccf6dddf443fb99e152a16a67009827c44a820597e98e585cb8b20(source). -
src.calls.REL_18_6.42P03— resolved core call groups; SHA-2569ee8a0e81d8f0825c5c1ae45583439859a26e602bdd4ce2f2a62aa278867ccbf. -
authored evidence— source claims, message roles, and runtime boundary.