PostgreSQL error 34000 occurs when you reference a cursor that does not exist, has not been declared, or is not accessible in the current scope. Ensure the cursor is declared before use and accessed within the same transaction where it was created.
Error 34000 indicates that PostgreSQL encountered a reference to a cursor name that does not exist or is not accessible in the current context. In PostgreSQL, cursors are transaction-specific objects that must be explicitly declared with the DECLARE statement before they can be used. When you attempt to FETCH from, MOVE on, or CLOSE a cursor that was never declared, has already been closed, or is outside the scope of the current block or transaction, PostgreSQL raises error 34000. This error commonly occurs in PL/pgSQL functions, stored procedures, and multi-step SQL transactions where cursor lifecycle management is not properly handled.
Ensure that the cursor is declared with the DECLARE statement before any FETCH, MOVE, or CLOSE operations:
BEGIN;
DECLARE my_cursor CURSOR FOR SELECT * FROM users;
FETCH FROM my_cursor; -- Now valid
END;If the cursor is referenced before DECLARE, move the DECLARE statement to the beginning of the transaction or block.
PostgreSQL identifiers are case-insensitive by default unless quoted. If you declare a cursor with unquoted name, use the same unquoted name in FETCH/MOVE/CLOSE commands:
-- Declaration
DECLARE my_cursor CURSOR FOR SELECT * FROM users;
-- Use lowercase (folded to my_cursor internally)
FETCH FROM my_cursor; -- Correct
FETCH FROM MY_CURSOR; -- Also correct (case-folded)
-- If quoted, must match exactly:
DECLARE "MyCursor" CURSOR FOR SELECT * FROM users;
FETCH FROM "MyCursor"; -- Must use exact caseCursors exist only within the transaction where they were declared. Do not commit or rollback the transaction after declaring a cursor if you plan to fetch from it:
-- Correct: All in one transaction
BEGIN;
DECLARE my_cursor CURSOR FOR SELECT * FROM users;
FETCH FROM my_cursor;
COMMIT;
-- Wrong: Cursor lost after first transaction
BEGIN;
DECLARE my_cursor CURSOR FOR SELECT * FROM users;
COMMIT; -- Cursor closed here
FETCH FROM my_cursor; -- Error: cursor not foundIn PL/pgSQL functions and blocks, cursors must be declared in the DECLARE section or use refcursor variables:
DO $$
DECLARE
my_cursor CURSOR FOR SELECT * FROM users;
rec RECORD;
BEGIN
OPEN my_cursor;
LOOP
FETCH my_cursor INTO rec;
EXIT WHEN NOT FOUND;
RAISE NOTICE '%', rec;
END LOOP;
CLOSE my_cursor;
END $$;Note: Use OPEN instead of just declaring, and ensure CLOSE is called before the function ends.
If passing refcursor as a function parameter, ensure the cursor is opened in the calling context:
CREATE FUNCTION fetch_users(cur REFCURSOR) RETURNS SETOF RECORD AS $$
BEGIN
RETURN QUERY FETCH ALL FROM cur; -- cur must be opened by caller
END;
$$ LANGUAGE plpgsql;
-- Caller must open cursor before calling function
BEGIN;
DECLARE my_cursor CURSOR FOR SELECT * FROM users;
SELECT * FROM fetch_users(my_cursor);
COMMIT;Cursor Lifecycle and Transactions: PostgreSQL cursors are tightly bound to their declaring transaction. Unlike some databases that support scrollable or persistent cursors, PostgreSQL cursors are ephemeral—they exist only during the transaction that created them. When using application connection pooling, be aware that each application statement may run in a different connection with different transaction context. Always fetch cursor results within the same logical transaction. In PL/pgSQL, the distinction between DECLARE (for local cursors) and REFCURSOR (for return values) is important: local cursors are automatically closed when the function exits, while refcursors can be returned to the caller but must still be used before the original transaction commits. For long-running operations across multiple client requests, consider fetching all results into a temporary table or application array rather than relying on cursor state across requests.
ERROR: syntax error at end of input
Syntax error at end of input in PostgreSQL
Bind message supplies N parameters but prepared statement requires M
Bind message supplies N parameters but prepared statement requires M in PostgreSQL
Multidimensional arrays must have sub-arrays with matching dimensions
Multidimensional arrays must have sub-arrays with matching dimensions
ERROR: value too long for type character varying
Value too long for type character varying
insufficient columns in unique constraint for partition key
How to fix "insufficient columns in unique constraint for partition key" in PostgreSQL