> ## 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.

# エラー処理とリトライ

> difyctl のエラー時にエージェントを安全に失敗させる：終了コードで分岐し、エラーを解析し、一時停止したワークフローを検出し、安全なものだけをリトライ

エージェントが `difyctl` を安全に駆動するには、4 つの失敗処理ロジックが必要です。正しいチャネルを読む、終了コードで分岐する、一時停止したワークフローを成功として扱う、そして安全なものだけをリトライする、という 4 つです。

## 正しいチャネルを読む

プログラムからの呼び出しでは、必ず `-o json` を付けます。チャネルの規律は厳密で、その上にロジックを構築できます。

* **成功**：ペイロードは ANSI コードを含まない解析可能な JSON として stdout に出力され、stderr は空です。
* **失敗**：stdout は空で、stderr は構造化された JSON オブジェクトです。前後の空白を除いた stderr 全体が JSON として解析できます。
* **一時停止**：人間の入力を待って停止したワークフローも、成功チャネルで終了コード `0` を返し、stdout に `"status": "paused"` が含まれます。失敗ではなく [成功として扱います](#一時停止はエラーではなく成功)。

つまり解析ルールはこうです。まず終了コードを確認し、成功時は stdout に対して `JSON.parse(stdout)` を、失敗時は stderr に対して `JSON.parse(stderr)` を実行します。エラーオブジェクトのフィールドと完全なサンプルは [出力形式と終了コード](/ja/3.11.x/develop/cli/reference/output-formats-and-exit-codes#エラー) を参照してください。

## 終了コードで分岐する

終了コードの完全な一覧は [出力形式と終了コード](/ja/3.11.x/develop/cli/reference/output-formats-and-exit-codes#終了コード) を参照してください。エージェントにとって重要な分岐は次のとおりです。

* **終了コード `7`、レート制限**：サーバーが 429 を返しました。バックオフしてリトライします。
* **終了コード `4`、認証**：セッションがない、またはセッションが期限切れです。何かを行う前に、まずセッションを再確立します。同じコマンドをそのままリトライしないでください。呼び出しを無駄に消費するだけです。[エージェントが動作する場所で認証する](/ja/3.11.x/develop/cli/integrate-agents/auth-for-agent-deployments) を参照してください。
* **終了コード `1`、汎用エラーまたはサーバーエラー**：ネットワーク障害、サーバーエラー、アプリが見つからない、または不明なフラグやコマンドです。エラーオブジェクトを解析し、`error.code` を確認します。やみくもにリトライしないでください。
* **終了コード `2`、無効な入力**：CLI がリクエストを送信する前に値を拒否しました。`--inputs` の JSON が不正、アプリ ID が UUID でない、または `--limit 0` のような範囲外のフラグなどです。呼び出しを修正してください。そのままリトライしても同じように失敗します。
* **一時停止した実行の終了コードは `0`**：人間の入力ステップに達したワークフローは、エラーではなく、stdout に `"status": "paused"` を伴って終了コード `0` で終了します。これは [別途処理](#一時停止はエラーではなく成功) します。

## 一時停止はエラーではなく成功

人間の入力ステップを持つワークフローアプリは、実行の途中で一時停止します。コマンドは終了コード `0` で終了し、stdout に一時停止を報告します。stderr には捕捉すべきものは何もありません。終了コードしか確認しないエージェントは、一時停止を完了した実行と取り違えます。そのため、完了チェックはペイロードを読む必要があります。

```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"].
        ...
```

一時停止時の完全なペイロード、再開コマンド、有効期限のルールは [ワークフローが一時停止する場合](/ja/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` による上書きは [グローバルフラグ](/ja/3.11.x/develop/cli/reference/global-flags#http-リトライ) を参照してください。

自動でリトライされないのは POST であり、これがまさに重要な呼び出しです。すべての `run app` は POST です。`run app` が実行の途中で失敗したとき、CLI はサーバーが既に実行を開始したかどうかを判断できないため、デフォルトでは再送しません。

唯一のオプトインは [`run app --retry-on-limit`](/ja/3.11.x/develop/cli/reference/apps#アプリを実行) で、429 のときに限って有界のバックオフでリトライします。アプリの実行は冪等ではないため、デフォルトではオフです。

同じことがエージェントのロジックにも当てはまります。失敗した `run app` を再実行することは、古い実行の再開ではなく、新しい実行です。チャットボットであれば、たいてい許容できます（質問をやり直すだけです）。副作用を持つワークフローでは、ワークフローの動作に応じてリトライを制限してください。

エージェント側のリトライは上記の一時的なエラーに限定し、試行回数に上限を設け、リトライの判断をすべて記録してください。エージェントが書き込みを黙って再実行することは、[`effect` ラベル](/ja/3.11.x/develop/cli/reference/help#機械可読ヘルプ) が防ごうとしている失敗モードそのものです。`difyctl help -o json` のすべてのコマンドには `read`、`write`、`destructive` のいずれかのタグが付くため、ループはそのタグに基づいて自動リトライを制限でき、`write` を盲目的に再送することは決してありません。
