# 25008 — held_cursor_requires_same_isolation_level

> PostgreSQL SQLSTATE 25008: the held-cursor isolation-level condition and its source-only boundary.
---

# 25008 — held_cursor_requires_same_isolation_level

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

`25008` is reserved for a held cursor whose transaction isolation level is not compatible with the required operation. The fixed PostgreSQL 18.6 core scan has no direct report group for it.

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

| Field | Value |
| --- | --- |
| SQLSTATE | `25008` |
| Condition | `held_cursor_requires_same_isolation_level` |
| 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_HELD_CURSOR_REQUIRES_SAME_ISOLATION_LEVEL` |
| Aliases | `—` |

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

## Meaning {#meaning}

This is narrower than `24000` invalid cursor state and unrelated to the general `40001` serialization retry path. A cursor being held across transactions does not by itself prove that 25008 was emitted.

## Diagnosis {#diagnosis}

Capture the cursor declaration, holdability, isolation level of both transactions, and exact SQLSTATE from the actual reporting implementation. This fixed evidence provides no message template or copyable SQL trigger.

## Response {#response}

Keep the cursor and consuming transaction at a compatible isolation level, or close and re-declare it according to the API contract. Do not substitute a 24000 repair or a generic rollback without the actual state.

## Source message and runtime boundary {#messages}

The fixed `errcodes.txt` row defines the condition and macro; the resolved call scan in `raw/calls/REL_18_6.jsonl` contains no direct report group, so this batch publishes no message template and constructs no natural SQL trigger.

## Applicability boundary and case {#case}

The selected runner cases on PostgreSQL 18.6 and 10.21 are `not_applicable`; they record the source boundary and contain no copyable SQL trigger. [Structured evidence](../data/evidence/25008.json) · [case export](../data/cases/25008.json)

## Versions and limits {#versions}

The generated facts record the locked catalogue membership. The selected latest and PG10 cases intentionally remain source-only and `not_applicable`.

## Related {#related}

[24000 invalid cursor state](../24000/), [25004 branch transaction isolation](../25004/), [40001 serialization failure](../40001/).

## Sources {#sources}

- [`src.errcodes.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/errcodes.txt) (SHA-256 `6e8de346643ba84aa3c9c6a73360acfc7b2dfb89162c06c08ce9bf5bcd5bbcba`)
- `src.calls.REL_18_6` — `raw/calls/REL_18_6.jsonl` (SHA-256 `9ee8a0e81d8f0825c5c1ae45583439859a26e602bdd4ce2f2a62aa278867ccbf`)
