# 34000 — invalid_cursor_name（游标名称无效）

> PostgreSQL SQLSTATE 34000：游标生命周期、会话归属与恢复。
---

# 34000 — invalid_cursor_name（游标名称无效）

## 速览 {#at-a-glance}

`34000` 表示当前后端会话无法解析游标或 portal 名称。已经关闭的游标、在另一个连接池会话声明的游标、或事务结束后消失的游标，都不是“游标存在但位置错误”（`24000`）。

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

| 字段 | 值 |
| --- | --- |
| SQLSTATE | `34000` |
| 条件名 | `invalid_cursor_name` |
| 状态 | `有效` |
| 已知存在于 | `7.4` |
| 锁定快照 | `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` |
| 宏 | `ERRCODE_INVALID_CURSOR_NAME, ERRCODE_UNDEFINED_CURSOR` |
| 别名 | `ERRCODE_UNDEFINED_CURSOR` |

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

## 含义 {#meaning}

执行器的 portal 路径在 `FETCH` 找不到命名 portal 时报告 `cursor "%s" does not exist`。游标属于后端会话；普通游标还受事务生命周期约束，除非明确声明合适的 hold 选项。因此连接池必须在预期事务边界内保持声明和使用都在同一连接。

## 诊断 {#diagnosis}

记录 SQLSTATE、动态游标名称、后端 PID 和事务状态。检查应用是否提前 `CLOSE`、发生隐式提交、把连接归还池中，或已经换到另一会话。同一后端上的 `pg_cursors` 和会话身份可以帮助诊断；新连接不能查看或抓取旧连接拥有的游标。

## 处理 {#response}

显式事务中的 `FETCH` 失败后，先回滚再发后续命令。在同一会话重新声明游标；如果工作必须跨连接池 checkout，就改为物化键或结果。只有确实需要提交后继续读取时才使用 holdable cursor；它不会让游标跨会话可用。

## 报文 {#messages}

选定核心路径使用 `cursor "%s" does not exist`；协议和 PL/pgSQL 路径可能使用 `portal "%s"` 或相同的 cursor 措辞。名称是动态值，应使用 SQLSTATE 和诊断字段，而不是跨版本、跨调用者比较完整报文。

## 代表案例 {#case}

共享 registry `verify/cases/34000/snippets.json`（SHA-256 `ad5b519e8ca30fd2636b1d0bace32dc18558cccc7b9d360081c27b12a1405f08`）先声明并关闭游标，验证缺失 FETCH，再在同一会话回滚并重新声明。见[公开案例导出](../../data/cases/34000.json)和[结构化证据](../../data/evidence/34000.json)。

<!-- BEGIN SQLSTATE SNIPPET: cursor_lifecycle_recovery -->

```sql
BEGIN;
DECLARE cursor_name CURSOR FOR SELECT 1;
CLOSE cursor_name;
FETCH cursor_name;
ROLLBACK;
BEGIN;
DECLARE cursor_name CURSOR FOR SELECT 1;
FETCH cursor_name;
CLOSE cursor_name;
COMMIT;
```
<!-- END SQLSTATE SNIPPET -->

运行器会替换唯一游标名。第一次 `FETCH` 特意放在 `CLOSE` 之后；第二次在新的 `BEGIN` 和声明之后执行，成功返回数据，证明是生命周期修复而非客户端模拟。

选定案例在 PostgreSQL 18.6 和 10.21 关闭命名游标后执行 `FETCH`，观察到 `34000`。失败块进入 `INERROR`；回滚后重新声明、抓取、关闭并提交，连接回到 `IDLE`。

## 版本 {#versions}

锁定目录在正式快照中都记录了该条件。18.6 和 10.23 源码扫描都包含 portal、PL/pgSQL 路径；选定运行在 18.6 与 10.21 观察到相同 SQLSTATE，但源码行和调用者措辞可能变化。

## 相关 {#related}

可对照[24000 游标状态无效](../24000/)以及另一个会话资源错误[26000 预备语句名称无效](../26000/)。

## 来源 {#sources}

- [`src.errcodes.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/errcodes.txt)（SHA-256 `6e8de346643ba84aa3c9c6a73360acfc7b2dfb89162c06c08ce9bf5bcd5bbcba`）
- [`src.portalcmds.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/commands/portalcmds.c#L197-L199)（SHA-256 `e71c5bdb2da67771fb5f42f18b823e8af3ee6c31d5314e97a47ad18f7788bf35`）
- [`DECLARE 官方文档`](https://www.postgresql.org/docs/18/sql-declare.html) · 本地调用扫描 `src.calls.REL_18_6`（SHA-256 `9ee8a0e81d8f0825c5c1ae45583439859a26e602bdd4ce2f2a62aa278867ccbf`）
