StateGraph 节点返回列表时报 Expected dict,先查漏改的 return
StateGraph 使用字典状态时,普通节点应返回状态更新字典。检查正常、空结果及异常分支的 return,把检索列表放进正确字段,再离线核对最终状态。
StateGraph 节点返回列表时报 Expected dict,先查漏改的 return
图可以正常编译,一跑到检索节点却报:
InvalidUpdateError: Expected dict, got ['whoops']
如果节点使用 TypedDict 状态,先看报错节点实际返回了什么。检索结果可以是列表,但普通节点交给图的返回值应是状态更新字典。例如状态字段叫 documents,应返回 {"documents": documents},不能直接把 documents 交出去。
这个区别容易在空结果分支被漏掉:正常检索返回字典,没搜到时顺手写了 return []。于是同一张图只对部分问题失败,看起来像模型输出不稳定。
把模型拿掉,先跑这张小图
下面没有模型请求,也不需要 API key。需要环境已安装 langgraph;版本诊断可以运行 python -m pip show langgraph langchain-core。下方示例适用于 StateGraph 字典状态接口,离线验收版本为 langgraph 1.2.14。
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
class State(TypedDict):
query: str
documents: list[str]
status: str
def retrieve(state: State) -> dict:
if not state["query"].strip():
return {"documents": [], "status": "empty_query"}
# 固定数据仅用于验证图的状态更新,不代表真实检索结果。
docs = ["订单保留 30 天"] if "订单" in state["query"] else []
return {"documents": docs, "status": "ok" if docs else "no_match"}
builder = StateGraph(State)
builder.add_node("retrieve", retrieve)
builder.add_edge(START, "retrieve")
builder.add_edge("retrieve", END)
graph = builder.compile()
for query, expected in [("订单", "ok"), ("天气", "no_match"), (" ", "empty_query")]:
result = graph.invoke({"query": query, "documents": [], "status": "new"})
assert result["status"] == expected
assert isinstance(result["documents"], list)
print("three branches passed")
这段测试故意覆盖了三条路。只测能命中资料的正常问题,抓不到空列表直接返回的故障。
回到原来的节点检查什么
从节点入口走一遍每个 return,尤其看提前退出、except、缓存命中三个地方。缓存里存的是结果列表,取出后仍需要包装成状态更新;except 分支如果允许继续执行,也要返回明确定义的错误状态。不要为了消除类型错误吞掉异常,否则后续节点可能把失败当成“没有相关资料”。
还要检查包装的键名是否属于状态定义。把 return docs 改成 return {"result": docs},但图里实际字段是 documents,修复依然没有对上接口。验收时直接断言最终 documents 的值,不要只看进程是否退出。
如果原节点已经返回字典,堆栈却显示收到了列表,继续查注册到图里的包装函数。有时修的是业务函数,add_node 实际注册的是另一个适配器。
这里讨论普通状态更新节点。使用 Command 等专门控制对象时,需要按对应 API 判断,不能用“所有节点只能返回 dict”去否定这些合法入口。


