# 42P11 — Invalid cursor definition

> Source-backed reference for PostgreSQL SQLSTATE 42P11.
---

# 42P11 — Invalid cursor definition

## At a glance {#at-a-glance}

`42P11` (**invalid_cursor_definition**) Cursor options and cursor plans have definition-time constraints.

<!-- BEGIN SQLSTATE FACTS: generated by scripts/generate.py; do not edit -->

| Field | Value |
| --- | --- |
| SQLSTATE | `42P11` |
| Condition | `invalid_cursor_definition` |
| 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_INVALID_CURSOR_DEFINITION` |
| Aliases | `—` |

<!-- source facts: data/errcodes/42P11.json -->
<!-- END SQLSTATE FACTS -->

## Meaning {#meaning}

A cursor declaration or SPI cursor request violates a definition-time rule. Core 18.6 has separate paths for an SPI plan with more than one query, conflicting `SCROLL`/`NO SCROLL` or `ASENSITIVE`/`INSENSITIVE` options, and an `INSENSITIVE` cursor combined with a row-locking clause; the latter also reports that insensitive cursors must be READ ONLY.

## Diagnosis {#diagnosis}

Preserve the exact option named by the error. Check whether the source is DECLARE or an SPI caller, whether the SPI plan contains more than one statement, and whether the option pair is `SCROLL`/`NO SCROLL` or `ASENSITIVE`/`INSENSITIVE`. For `INSENSITIVE`, inspect row-locking clauses and the required READ ONLY contract; do not diagnose this as a missing cursor.

## Response {#response}

Split a multi-query SPI plan before opening a cursor, remove only the conflicting option, and add READ ONLY or remove the row lock when the INSENSITIVE contract requires it. If the caller is SPI, fix plan construction rather than changing cursor fetch code. An ERROR inside an explicit transaction requires `ROLLBACK` or `ROLLBACK TO` a pre-error savepoint before another command; autocommit can issue the corrected cursor request after the failed statement returns to idle.

## Messages {#messages}

Representative source messages include: message: `cannot open multi-query plan as cursor`; message: `DECLARE INSENSITIVE CURSOR ... %s is not valid`; DETAIL: `Insensitive cursors must be READ ONLY.`; message: `cannot specify both %s and %s`. Placeholders are filled by the actual object, column, or parameter.

## Versions {#versions}

The locked catalogue shows this condition by PostgreSQL 7.4; behavior here is fixed to PostgreSQL 18.6 source. Catalogue presence is a range boundary, not proof that every message or feature began in that release.

## Related {#related}

- [`42P18`](../42p18/)
- [`42P02`](../42p02/)

## Sources {#sources}

Source messages, line anchors, and evidence limits are recorded in [authored evidence](../data/evidence/42p11.json).

- `src/backend/executor/spi.c:1600-1602` ([fixed source](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/executor/spi.c#L1600-L1602))
- `src/backend/parser/analyze.c:3097-3104` ([fixed source](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/parser/analyze.c#L3097-L3104))
- `src/backend/parser/analyze.c:3040-3044` ([fixed source](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/parser/analyze.c#L3040-L3044))
- `src/backend/parser/analyze.c:3048-3052` ([fixed source](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/parser/analyze.c#L3048-L3052))
