当一个节点因外部 API 响应缓慢、瞬时网络错误或未处理的异常而失败时,LangGraph 提供了三种可组合的机制来应对:
  • 重试 — 根据异常类型和退避设置自动重新运行失败的尝试
  • 超时 — 限制单次尝试可以运行的时长
  • 错误处理 — 在所有重试耗尽后运行恢复函数
使用 setNodeDefaults 一次性为所有节点配置这些机制,而不是在每个 addNode 调用中重复配置。 这些机制按固定顺序组合:当一个节点尝试引发任何异常(包括来自超时的 NodeTimeoutError)时,重试策略决定是否重试。只有在重试耗尽后,错误处理器才会运行。 有关在超步(superstep)边界处干净地停止运行并在稍后恢复,请参阅优雅关停。
每个节点的超时和节点级错误处理器需要 @langchain/langgraph>=1.4.0。

重试

重试策略根据异常类型和退避设置自动重新运行失败的节点尝试。 向 addNode 传递 retryPolicy:

默认行为

重试是选择加入的。节点只有在直接配置了 retryPolicy 或通过图形默认值使用 setNodeDefaults 配置时才会重试。一个空的策略({})就足够了。没有策略时,第一次失败就会结束尝试,LangGraph 不会调用 retryOn。 如果策略省略了 retryOn,LangGraph 将使用一个内置处理器,该处理器会对抛出的错误进行重试,但以下情况除外:
  • 中止和取消错误:error.name === "AbortError",或者 error.message 以 "Cancel" 或 "AbortError" 开头
  • GraphValueError,通过 error.name 匹配
  • 中止的连接:error.code === "ECONNABORTED"
  • 状态码为 400、401、402、403、404、405、406、407 或 409 的 HTTP 客户端错误,从 error.response?.status 或 error.status 读取,适用于 fetch、Axios 等类似客户端
  • OpenAI 风格的配额错误:error.error?.code === "insufficient_quota"
其他 HTTP 状态码,包括 408 和 5xx 响应,都是可重试的,除非您覆盖了 retryOn。NodeTimeoutError 不在此阻止列表上,因此当配置了重试策略时它是可重试的。 某些失败会绕过 retryOn。图控制流错误,如 GraphInterrupt 和 Command 路由,会向上冒泡而不重试。中止运行的信号也会停止重试循环,即使 retryOn 会返回 true。

参数

自定义重试逻辑

向 retryOn 传递一个可调用对象。与 Python 不同,没有导出的 defaultRetryOn 辅助函数——实现您自己的谓词:

检查重试状态

在节点内部使用执行信息来检查当前尝试次数。当主调用持续失败时,这对于切换到备用方案非常有用:
executionInfo 暴露了以下字段: 即使没有重试策略,executionInfo 也可用——nodeAttempt 默认为 1。

超时

需要 @langchain/langgraph>=1.4.0。
addNode 上的 timeout 参数限制单个节点尝试可以运行的时长。传递一个数字(毫秒)或一个 TimeoutPolicy 以设置单独的运行和空闲限制:

运行超时

runTimeout 是对单次尝试的硬性挂钟时间限制。无论节点活动如何,它永远不会被刷新:
当超过限制时,LangGraph 会抛出 NodeTimeoutError,清除失败尝试的任何写入,并让重试策略决定是否重试。

空闲超时

idleTimeout 是一个进度重置限制。它只在节点在指定持续时间内停止产生可观察的进度时才会触发——与 runTimeout 不同,当时钟重置时,节点会产生一个进度信号:
您可以同时设置 runTimeout 和 idleTimeout。先触发的那个将取消尝试。

进度信号

在默认的 refreshOn: "auto" 下,空闲时钟在以下任何情况下重置:
  • 通过图写入路径进行状态写入
  • 通过 runtime.writer 进行自定义流输出
  • 子任务调度
  • 来自节点或其后代的任何 LangChain 回调事件(LLM token、工具调用、链开始/结束等)

心跳模式

设置 refreshOn: "heartbeat" 以将刷新源限制为仅限显式的 runtime.heartbeat() 调用。当您想要一个严格的空闲定义,不被话多的下属重置时,这非常有用:

手动心跳

对于不自然发出进度信号的长时间运行的工作,调用 runtime.heartbeat() 来手动重置空闲时钟:
runtime.heartbeat() 在空闲计时尝试之外是无操作的,因此您可以无条件地调用它。

NodeTimeoutError

当超时触发时,LangGraph 会抛出 NodeTimeoutError,并附带关于触发了哪个限制的结构化上下文: 使用 isNodeTimeoutError(error) 在 TypeScript 中缩小捕获的错误类型。 NodeTimeoutError 默认是可重试的。将 timeout 与重试策略结合使用开箱即用——超时时钟在每次新尝试时重置,并且超时尝试的写入在下次重试前被清除:

使用 Send 的动态超时

当使用 Send 动态分派节点时(例如,在 map-reduce 模式中),您可以直接在 Send 上传递一个超时,以覆盖该特定推送的静态超时:
如果 Send 上省略了超时,则应用目标节点的超时(在 addNode 时设置)。这允许您在节点上设置默认超时,并为单个调用收紧它。

错误处理

需要 @langchain/langgraph>=1.4.0。
错误处理器在节点失败且所有重试耗尽后运行。它接收当前状态,可以更新状态或使用 Command 路由到不同的节点。这对于补偿流程(Saga 模式)非常有用,您可以在其中优雅地恢复,而不是中止整个图。 向 StateGraph(而不是基类 Graph)上的 addNode 传递 errorHandler:
处理器仅在重试策略耗尽后触发,或者如果未配置重试策略则立即触发。重试策略和错误处理器是解耦的:独立配置何时重试以及何时补偿。

NodeError

错误处理器通过类型化的 error: NodeError 参数接收失败上下文:
NodeError 是一个具有两个字段的类: error: NodeError 参数是选择加入的。不需要失败上下文的处理器可以省略第二个参数,只接受 state。

使用 Command 进行路由

错误处理器可以返回一个 Command 来更新状态并路由到特定节点,从而实现 Saga / 补偿模式:
chargePayment 会针对 ConnectionError 最多重试 3 次。如果重试耗尽(或错误不是 ConnectionError),处理器将通过更新状态并路由到 finalize 来补偿,而不是中止图。

恢复安全的失败

失败来源被检查点化。如果图在节点失败后但在处理器完成前被中断或进程崩溃,当图从其检查点恢复时,处理器将看到相同的 NodeError 上下文。

与 interrupt() 的行为

在节点内部引发的 interrupt() 不会路由到错误处理器。中断使用 GraphBubbleUp 机制来暂停图执行,用于人在环中的工作流程,绕过重试策略和错误处理器。图照常暂停。

子图失败

如果一个节点包装了一个子图,并且子图引发了未处理的异常,该异常将浮出到父节点。如果父节点有一个错误处理器,处理器将在 error.error 中包含子图的异常并被触发。

图默认值

需要 @langchain/langgraph>=1.4.0。
与其在每个 addNode 调用中重复相同的 retryPolicy、errorHandler、timeout 或 cachePolicy,不如使用 setNodeDefaults 在一个地方配置图形范围的默认值:
stepA 和 stepB 现在共享相同的重试策略、错误处理器、超时和缓存策略,没有任何重复。

优先级

直接传递给 addNode() 的每个节点值始终覆盖由 setNodeDefaults() 设置的默认值。默认值在 compile() 时解析,因此您可以按任意顺序在 addNode() 之前或之后调用 setNodeDefaults():

默认错误处理器

errorHandler 默认值在每次图形运行都映射到外部过程(例如后台作业行)时特别有价值,并且任何未处理的节点失败都应该将该过程标记为失败,而无需在每个 addNode 上重复 errorHandler。当某个步骤需要自己的补偿逻辑时,每个节点的处理器仍然优先:
如果 fetchData 在重试后失败,markProcessFailed 会运行。如果 chargePayment 在重试后失败,则 refundPayment 会运行,因为每个节点的处理器覆盖了默认值。

适用性矩阵

并非所有默认值都适用于所有节点类型。错误处理器节点(通过 addNode(..., { errorHandler }) 注册的那些)被排除在某些默认值之外,以防止不安全的行为:

范围

父图上设置的默认值不会被子图继承。每个图维护自己的默认值。

函数式 API

task 和 entrypoint 上提供了 timeout 选项;task 还接受 retry 选项(不是 retryPolicy):
行为与 addNode 匹配:超时时引发 NodeTimeoutError,缓冲的写入被清除,重试策略决定是否重试。JavaScript/TypeScript SDK 中的 task / entrypoint 不提供错误处理器——请改用 StateGraph.addNode(..., { errorHandler })。

优雅关停

协作关停允许您在当前超步完成后停止正在运行的图形运行,并保存一个可恢复的检查点。这对于处理 SIGTERM 信号或任何需要回收资源而不丢失工作的外部监督器非常有用。
需要 @langchain/langgraph>=1.4.0。
创建一个 RunControl 并将其作为 control 传递给 invoke 或 stream。从任何上下文调用 requestDrain() 以表示运行应停止:

语义

排空是协作的,并在超步之间进行,不会抢占已经运行的工作:

排空后恢复

使用相同的 thread_id 通过 invoke(null, config) 恢复已排空的运行:

在节点内读取排空状态

通过 runtime 参数访问排空状态,以在达到超步边界之前调整节点行为:

SIGTERM 钩子模式

处理进程关闭的推荐模式:
requestDrain() 不会取消进行中的异步工作。要设置硬上限,请将排空与优雅超时和 AbortSignal 结合使用。

限制

  • setNodeDefaults 不会被子图继承:每个图独立管理自己的默认值。
  • 错误处理器仅限 StateGraph:将 errorHandler 传递给 StateGraph.addNode,而不是基类 Graph。错误处理器在 task / entrypoint 上不可用。
  • 每个节点一个处理器:每个节点最多可以有一个 errorHandler。
  • 处理器失败会向上冒泡:如果错误处理器本身抛出异常,该异常会像节点没有处理器一样传播。