Incident brief
Duplicate cursor
Declaring a cursor with a name already open in the same transaction raises this error; cursor names must be unique within a transaction.
What lands in your log
ERROR: cursor "dc" already exists
In 10 seconds
- What triggers it
- Open a transaction and DECLARE a cursor with a given name.
- Fix
- Give each cursor a unique name within the transaction.
- Proof
- Reproduced on PostgreSQL 18.4 → Inside one transaction the reproduction declares a cursor, then re-declares the same name: the second DECLARE raises SQLSTATE 42P03.
Fix
What to do right now
Application-level steps for this error.
- Give each cursor a unique name within the transaction.
- Or CLOSE the existing cursor before re-declaring the name.
-- Use a distinct name for each open cursor.
BEGIN;
DECLARE dc CURSOR FOR SELECT 1;
DECLARE dc2 CURSOR FOR SELECT 2;
COMMIT;For this error
See this error live on the server
Run these against the affected instance to confirm the diagnosis before you act.
Duplicate cursor 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 18 Documentation, Appendix A. PostgreSQL Error Codes (Table A.1, Class 42, Syntax Error or Access Rule Violation)
42P03 → duplicate_cursorRead the full section on postgresql.org →
Re-declaring an open cursor name
The cursor 'dc' is already open in this transaction, so the second DECLARE raises 'cursor dc already exists'.Recovering the aborted transaction
Because DECLARE ran inside an open transaction, that transaction is now aborted and must be rolled back before session B can issue a new command.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.
- 1Open a transaction and DECLARE a cursor with a given name.
- 2DECLARE another cursor with the same name in the same transaction.
- 3The second DECLARE aborts with SQLSTATE 42P03: 'cursor already exists'.
A routine looped and re-declared a fixed cursor name within the same transaction, colliding on the second pass.
-- The error is self-contained in one statement; no schema is required.
SELECT 'no schema needed' AS setup_note;BEGIN;
DECLARE dc CURSOR FOR SELECT 1;
DECLARE dc CURSOR FOR SELECT 2;ROLLBACK;
SELECT 'ok' AS session_after_rollback;What PostgreSQL actually returned
setup_note
------------------
no schema needed
(1 row)BEGIN
DECLARE CURSOR
ERROR: cursor "dc" already existsROLLBACK
session_after_rollback
------------------------
ok
(1 row)Two cursors coexist once they have distinct names.
Without this
Before: re-declaring the name collides
With this, tested
After: two distinct names both declare
- 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.
Related errors
Verification
- Last verified
- 2026-07-24 (isolated lab, PostgreSQL 18.4)
- Verification scope
- Verified against PostgreSQL 18.4 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.