> ## Documentation Index
> Fetch the complete documentation index at: https://enterprise-docs.dify.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 处理错误与重试

> 让 Agent 在 difyctl 出错时安全失败：根据退出码分支处理、解析错误、检测已暂停的工作流，并只重试安全的操作

要让 Agent 安全地驱动 `difyctl`，需要四类失败处理逻辑：读取正确的通道、根据退出码分支处理、把已暂停的工作流当作成功，并只重试安全的操作。

## 读取正确的通道

每次程序化调用都加上 `-o json`。通道规则非常严格，可据此构建逻辑：

* **成功**：负载以可解析的 JSON 形式输出到 stdout，不含 ANSI 码，stderr 为空。
* **失败**：stdout 为空，stderr 是一个结构化的 JSON 对象。整段去除首尾空白的 stderr 都可解析为 JSON。
* **暂停**：因等待人工介入而停止的工作流也会在成功通道上以退出码 `0` 结束，stdout 上带有 `"status": "paused"`。应 [将其视为成功](#暂停意味着成功，而非错误)，而非失败。

因此解析规则是：先看退出码，成功时对 stdout 执行 `JSON.parse(stdout)`，失败时对 stderr 执行 `JSON.parse(stderr)`。错误对象的字段及完整示例详见 [输出格式与退出码](/zh/3.11.x/develop/cli/reference/output-formats-and-exit-codes#错误)。

## 根据退出码分支处理

完整的退出码表详见 [输出格式与退出码](/zh/3.11.x/develop/cli/reference/output-formats-and-exit-codes#退出码)。对 Agent 来说，需要关注的分支有：

* **退出码 `7`，限流**：服务器返回了 429。退避后重试。
* **退出码 `4`，认证**：没有会话，或会话已过期。先重建会话，再做其他任何事。不要原样重试同一命令，那只会白白消耗调用次数。参见 [在 Agent 运行的位置进行认证](/zh/3.11.x/develop/cli/integrate-agents/auth-for-agent-deployments)。
* **退出码 `1`，通用错误或服务器错误**：网络故障、服务器错误、应用未找到，或未知的标志或命令。解析错误对象并检查 `error.code`。不要盲目重试。
* **退出码 `2`，输入无效**：CLI 在发出任何请求前就拒绝了某个值：`--inputs` JSON 格式错误、应用 ID 不是 UUID，或像 `--limit 0` 这样超出范围的标志。修正调用；原样重试只会以同样的方式失败。
* **已暂停的运行退出码为 `0`**：触发人工介入步骤的工作流会以退出码 `0` 结束，stdout 上带有 `"status": "paused"`，并非错误。该情况 [单独处理](#暂停意味着成功，而非错误)。

## 暂停意味着成功，而非错误

带有人工介入步骤的工作流应用会在运行中途暂停。命令以退出码 `0` 结束，并在 stdout 上报告暂停。stderr 上没有任何可捕获的内容。只检查退出码的 Agent 会把暂停误判为已完成的运行，因此完成检查必须读取负载：

```python theme={null}
import json, subprocess

r = subprocess.run(
    ["difyctl", "run", "app", app_id, "--inputs", json.dumps(inputs), "-o", "json"],
    capture_output=True, text=True,
)
if r.returncode == 0:
    payload = json.loads(r.stdout)
    if payload.get("status") == "paused":
        # Success-with-pending: collect input, then resume with
        # payload["form_token"] and payload["workflow_run_id"].
        ...
```

完整的暂停负载、恢复命令以及过期规则详见 [工作流暂停时](/zh/3.11.x/develop/cli/reference/apps#工作流暂停时)。恢复后的运行可能在后续步骤再次暂停，因此每次执行 `resume app` 后都要运行同样的检查。

## 根据 `error.code` 分支处理

错误对象的 `error.code` 是一个稳定的机器标识符：同一种失败在多次调用中产生相同的码，因此分支逻辑只需写一次。按恢复动作而非逐一枚举每个码来分组：

* **重新认证后重试**：`not_logged_in`、`auth_expired`。两者都退出码 4。
* **退避后重试**：`network_connection`、`server_5xx`。临时性的基础设施问题。
* **不重试，检查原因**：`server_4xx_other`。服务器拒绝了请求：应用 ID 错误、输入有误，或权限不足。`message` 中带有服务器给出的原因。
* **修正调用方式**：随退出码 2 一同到达的用法类错误码。

错误对象还带有可读的 `hint`，提供建议的恢复动作。记录下来可加快调试。

当失败来自服务器时，错误对象可能还包含 `error.server`，即服务器自身的错误体。如果你的循环需要这种粒度，其 `server.code`（例如 `not_found`）能比 `server_4xx_other` 更精细地区分拒绝原因。

## 谨慎重试

`difyctl` 已经会在临时失败时以指数退避重试幂等请求（GET、PUT、DELETE）。预算及 `--http-retry` 覆盖项详见 [全局标志](/zh/3.11.x/develop/cli/reference/global-flags#http-重试)。

它从不自动重试的是 POST，而这恰恰是关键调用：每个 `run app` 都是 POST。当 `run app` 中途失败时，CLI 无法判断服务器是否已经开始执行，因此默认不会重新发送。

唯一的选择项是 [`run app --retry-on-limit`](/zh/3.11.x/develop/cli/reference/apps#运行应用)，它专门在遇到 429 时以有界退避重试。它默认关闭，因为应用运行并非幂等。

同样的道理也适用于 Agent 的逻辑：重新运行一个失败的 `run app` 是一次新的执行，而不是对旧执行的恢复。对聊天助手来说，这通常可以接受（重新提问即可）。对带有副作用的工作流，则要根据工作流的行为来决定是否重试。

Agent 侧的重试只用于上述临时性错误，限制尝试次数，并记录每一次重试决策。Agent 静默地重新运行写操作，正是 [`effect` 标签](/zh/3.11.x/develop/cli/reference/help#机器可读帮助) 要防范的失败模式。`difyctl help -o json` 中的每个命令都标记为 `read`、`write` 或 `destructive`，因此你的循环可以根据该标签来限定自动重试，绝不盲目重新发送 `write` 操作。
