---
type: study
created: 2026-08-11
updated: 2026-08-11
sensitivity: standard
status: verified
tags:
  - study
  - langchain
  - langgraph
  - python
  - errors
  - troubleshooting
topic: LangGraph Python 状态、持久化与子图错误排障
sources:
  - https://docs.langchain.com/oss/python/langgraph/errors/GRAPH_RECURSION_LIMIT
  - https://docs.langchain.com/oss/python/langgraph/errors/INVALID_CHAT_HISTORY
  - https://docs.langchain.com/oss/python/langgraph/errors/INVALID_CONCURRENT_GRAPH_UPDATE
  - https://docs.langchain.com/oss/python/langgraph/errors/INVALID_GRAPH_NODE_RETURN_VALUE
  - https://docs.langchain.com/oss/python/langgraph/errors/MISSING_CHECKPOINTER
  - https://docs.langchain.com/oss/python/langgraph/errors/MULTIPLE_SUBGRAPHS
last_verified: 2026-08-11
---

# LangGraph 状态、持久化与子图错误排障

## 六类错误对应六个不变量

| 错误 | 被破坏的不变量 | 修复方向 |
|---|---|---|
| `GRAPH_RECURSION_LIMIT` | 图必须在允许步数内到达停止条件 | 先找无界循环，再评估提高 limit |
| `INVALID_CHAT_HISTORY` | 每个 AI tool call 都有匹配 ToolMessage | 修正历史或从 interrupt 正确恢复 |
| `INVALID_CONCURRENT_GRAPH_UPDATE` | 同一步并行写同一 key 必须有合并规则 | 为 state key 定义 reducer |
| `INVALID_GRAPH_NODE_RETURN_VALUE` | 节点的所有路径都返回 state update dict | 修正返回类型与 key |
| `MISSING_CHECKPOINTER` | 使用内建 persistence 时必须配置 checkpointer | compile/entrypoint 时注入 |
| `MULTIPLE_SUBGRAPHS` | 同节点多次调用持久子图时 namespace 必须不冲突 | 选择正确 checkpointer 模式/唯一节点名 |

## 循环与步数上限

递归上限表示 StateGraph 在达到 stop condition 前执行了过多步骤。优先检查边条件、计数器、失败重试边和 A↔B 循环；只有确认复杂图确实需要更多合法步骤时，才在 invoke config 中提高 `recursion_limit`。

上限是防止失控成本的安全阀。提高到很大值必须同时设置模型/工具调用预算、wall-clock timeout、取消与告警，否则只是把无限循环推迟。

## Chat history 与 interrupt 恢复

`create_agent` 的 `call_model` 收到带未回答 tool calls 的历史会报错。常见来源：手工构造了不完整消息，或图在 tools 前中断/工具异常后，又带一条新 HumanMessage 从 START 调用。

处理有两条路径：

1. 为未回答的每个 tool call 补匹配 `ToolMessage`；直接以这些消息 invoke 会把它们追加并从 START 运行。
2. 读取 `get_state(config)`，删除未回答 tool calls 或补齐 ToolMessage，`update_state` 后用 `invoke(None, config)` 从 interrupt 恢复。

生产系统应优先使用第二条恢复语义，保留 checkpoint 与审计；不能在不理解状态位置时简单追加新用户消息。

## 并行更新与节点返回值

同一 superstep 的 fan-out 节点若同时返回同一 state key，而该 key 没有 reducer，LangGraph 无法确定覆盖顺序。将字段声明为带 reducer 的 `Annotated` 类型，例如 list 使用 append/`operator.add`；reducer 必须满足确定性与尽量可结合，否则重放或并行顺序变化会产生不同结果。

每个节点必须返回 dict，且 key 属于 state schema。复杂分支、异常处理和提前返回都要覆盖；不要返回裸 list、模型消息或 `None`。单测应逐分支断言返回 shape，而不只测 happy path。

## Checkpointer 与 persistence

直接使用 OSS Graph API 的内建持久化时，在 `StateGraph.compile(checkpointer=...)` 或 Functional API `@entrypoint(checkpointer=...)` 注入 checkpointer。`InMemorySaver` 适合测试，不适合进程重启后的生产恢复；Agent Server 可托管持久化基础设施。

thread ID、checkpoint namespace、序列化版本和数据库迁移必须作为运行契约。配置了 checkpointer 但每次随机 thread ID，与完全没有会话恢复在效果上近似。

## 多次调用子图的三种模式

同一节点内多次调用子图时，按需求选择：

- 不需要 interrupt：`checkpointer=False`，完全关闭子图 checkpoint。
- 需要 interrupt、但不需要跨调用持久：省略参数，继承父图；每次调用获得独立 namespace。
- 需要跨调用持久：`checkpointer=True`。系统使用位置后缀区分调用；若需要稳定、基于名称的 namespace，把每个子图包在唯一命名节点中。

不要因为遇到 `MULTIPLE_SUBGRAPHS` 就一律关闭 checkpoint；这会同时失去 interrupt/恢复能力。应先决定业务所需的持久化范围。

## 生产验证

- 构造故意循环，验证 recursion limit、成本上限与取消。
- 让并行节点同时写同一 key，验证 reducer 在不同完成顺序下结果一致。
- 在 tool 前 interrupt，分别测试补 ToolMessage、update_state + `invoke(None)`。
- 重启进程/实例后恢复同一 thread，验证真实持久 checkpointer，而非内存假象。
- 并发调用同一子图，检查 namespace、checkpoint 隔离和 time travel 结果。

