Dynamic Workflows 对 API 层面的失败有两级容错,429(速率限制)和 terminal API error 都落在这个体系里,不需要整个工作流从头重跑。

单个 agent() 调用级别

runtime 对每次 agent() 调用内置重试逻辑,遇到 429 会自动退避重试;如果重试耗尽后仍失败(终态 API 错误),该次调用返回 null,而不是让整个脚本崩溃。脚本编写时通常会 .filter(Boolean) 过滤掉这些 null 结果,所以少数子 Agent 因限流失败不影响其余 Agent 继续跑,也不会让整个运行判定为失败。

整个运行被打断/暂停

如果 429 触发的是更上层的中断(比如账号级限流导致运行被系统暂停,或者你手动停止了运行),恢复方式取决于是否还在同一 Claude Code 会话:

  • 同一会话内:/workflows 选中该运行按 p 恢复,或直接让 Claude “resume 那个工作流运行”。已完成的 Agent 直接返回缓存结果(不会重复调用、不会重复计费),只有未完成/失败的部分会重新执行。
  • 退出会话后:无法跨会话续跑,下一次会话运行同名工作流会从头开始。所以长时间大规模运行建议在同一会话里等它跑完或主动暂停,不要中途退出终端。

降低 429 复发概率

如果限流是并发过高导致的,可以在 /config 里调低 Dynamic workflow size(small/medium),或让 Claude 在描述任务时显式要求降低并发批次;也可以检查 /model 是否切到了配额更紧张的模型。

程序化场景(Agent SDK / Workflow 工具本身)还有一个 resumeFromRunId 参数:给 Workflow 调用传入之前运行的 run ID,未变化的 (prompt, opts) 组合直接复用缓存结果,只重跑改动过的或新增的 agent() 调用;使用前需要先用 TaskStop 停掉旧运行。这是同一会话内才有效的机制,本质上和 /workflows 里按 p 恢复是同一套缓存复用逻辑,只是暴露成了可编程参数。

429 vs 402:两种失败不能用同一招恢复

p 恢复能救回来的只是临时性失败——429 限流、瞬时网络错误、单次 API 抽风,这些重试几次或等一会就能过。但如果看到的是 Failed 状态下 API Error: 402 {"error":"API key credit spend allowance exhausted"},这是账户余额/额度耗尽,属于结构性失败,不是限流:

  • 402 代表这个 API key 的信用额度/预付余额已经花完了,runtime 的自动重试机制对此无效(不是能重试恢复的错误类型),所以子 Agent 直接以 Failed 收场,按 p 也不会有反应——因为恢复逻辑是”重新调用失败的 Agent”,而调用本身在余额不够的情况下永远会立即再报 402。
  • 恢复方式:先去对应的 API 控制台(Anthropic Console / Bedrock / Vertex / Foundry,取决于你接的哪个后端)给这个 key 充值或提额。
  • 如果暂时没法立刻加额度,也可以先用 /workflows 里的 x 把还没跑的 Agent/整个运行停掉,避免它反复重试原地报错刷 UI,等额度到账后再重新触发同一个工作流(同会话内可以 resumeFromRunId 复用已完成部分,跨会话就只能整个重跑)。

充值后按 p 没反应:Failed(终态)≠ Paused(暂停态)

p 的”暂停/恢复”只作用于你主动暂停的运行(状态显示 Paused)——官方文档原话是”从 /workflows 恢复暂停的运行”。而顶部状态栏显示 ✘ Failed 时,这个运行已经进入终态:整体判定为失败,不是”暂停中等你充完钱回来接着跑”的状态,所以 p 在 Failed 状态上不生效是预期行为,不是操作错了。

充完钱之后正确的继续方式:

  1. 直接在对话里让 Claude 续跑,说”恢复/重新运行刚才那个工作流”即可,不用去 /workflows 面板按键。Claude 会用同一份脚本重新触发运行,底层走的是 resumeFromRunId 机制——已经跑完的子 Agent 直接复用缓存结果(不重新调用、不重复计费),只有卡在 402 的那些会真正重新执行。这一步的前提是还在同一个 Claude Code 会话里;一旦退出过会话,缓存就没了,只能整个工作流重新跑一遍。
  2. 如果你想手动操作而不是让 Claude 重新触发:先确认 /workflows 里那次运行的状态——如果它仍显示某种”可恢复”的暂停态而不是纯 Failed,才轮到 p 生效;已经是终态 Failed 的运行本身在面板里通常没有”恢复”这个动作可选,只能重新发起。

“让 Claude 继续运行”却仍从头跑:两个常见原因

Workflow 工具真正的续跑接口是 resumeFromRunId——必须显式传入之前那次运行的 run ID(格式 wf_xxxxxx),Claude 才会走缓存复用;如果只是自然语言说”继续/恢复刚才的工作流”,Claude 未必会记得并主动带上这个参数,很容易直接当作新任务重新调用 Workflow 工具,于是整个脚本从头执行一遍。排查顺序:

  1. 确认还在同一个 Claude Code 会话:resumeFromRunId 明确是 same-session only。如果充值期间关过终端/重启过 Claude Code、或者会话因为长时间空闲被重置,之前那次运行的缓存已经不存在,任何方式都只能重新跑,这不是操作问题。
  2. 确认 run ID 还能找到:每次运行开始时 Claude 会收到一个写入 ~/.claude/projects/ 下的脚本路径和 run ID,运行失败时的工具结果里通常也带着这个 ID。如果对话历史里那条工具结果已经被压缩(context compaction)或滚出了上下文,Claude 就没有 ID 可用,只能重新发起。
  3. 明确指名要求 resume,而不是笼统说”继续”:比较可靠的说法是”用 resumeFromRunId 恢复 run wf_xxxxxx,不要重新发起新的运行”,把具体 run ID 带上,逼 Claude 走续跑分支而不是默认路径(新建运行)。如果你手头没有这个 ID,可以先问 Claude”刚才失败那次工作流的 run ID 是多少”,它如果还在同一会话上下文里能查到就会给你。

如果三点都满足(同会话、ID 还在、明确要求 resume)却依然从头跑,大概率是那次运行本身在 402 发生前完成的 agent() 调用太少(比如卡在很靠前的阶段),缓存能省下的工作量本来就有限,看起来像”重新跑”,其实是”该跑的都得跑”。

相关

  • claude-code-usage — Claude Code 中如何触发、观察和恢复运行
  • principles — 重放恢复的执行语义与运行时约束