# エラー

> Nodaro API のエラーは、HTTP ステータスと変更されないコードを 1 つのエンベロープで返します。各コードの意味、再試行すべきエラー、失敗したジョブが理由を伝える方法を説明します。

Source: https://nodaro.ai/ja/docs/developers/api/errors

**Nodaro API のエラー**は、どれも HTTP ステータスと、変更されない `code` を含む 1 つの JSON エンベロープを返します。そのため、クライアントはメッセージを解析せずに、コードで処理を分岐できます。失敗には 2 種類あります。呼び出し自体が拒否されて何も開始されない**リクエストエラー**と、開始した生成が後から失敗し、その理由をジョブで報告する**ジョブの失敗**です。

## エラーエンベロープ
```json
{ "error": { "code": "rate_limited", "message": "Too many requests. Max 30 per minute." } }
```

- `code` は、変更されない識別子（スラッグ）です。処理の分岐には、この値を使います。
- `message` は、人が読むためのテキストです。変わることがあるので、この値で照合しないでください。
- エラーによっては、フィールドが追加されます。追加されるフィールドは、以下の各コードに記載しています。ほとんどは `error` の中に入りますが、`already_running` では `executionId` が `error` と同じ階層に置かれます。また、実行前の利用枠のチェックに失敗した場合は、`required` と `remaining` がトップレベルに入ります。

唯一の例外は OAuth のトークンエンドポイントで、OAuth の形式で応答します。たとえば `{ "error": "invalid_grant", "error_description": "…" }` のような形式です。[OAuth アプリ](https://nodaro.ai/docs/developers/oauth)を参照してください。

## 再試行すべきエラー
| ステータス | 対処 |
| --- | --- |
| `5xx` | 通常は一時的なエラーです。指数バックオフで再試行します。 |
| `429` | 待ってから、バックオフしながら再試行します。たとえば、5 秒、10 秒、20 秒と待つ時間を延ばします。`Retry-After` ヘッダーがある場合は、その値に従ってください。 |
| `402` | クレジットを追加するか、ワークスペースの管理者に予算を依頼してから、再試行します。 |
| その他の `4xx` | まずリクエストを修正します。同じリクエストは、再び失敗します。 |

次の 3 つのサーバーエラーには、注意が必要です。

- `503 price_not_configured` は、再試行しても成功しません。このデプロイ環境の運用者が料金を設定するまで、そのモデルには料金がないためです。
- 非同期の構造化出力 LLM ルートでの `503 provider_unavailable` は、言語モデルの呼び出しを nodaro.ai に送るセルフホスティング環境では、恒久的なエラーです。
- SNS への投稿では、`503 publish_retryable` は何も投稿されていないことを示し、同じリクエストを安全に再送できます。一方、`500 publish_failed` は結果が不明であることを示し、再送すると二重に投稿されるおそれがあります。

## エラーコード
### 400 Bad Request
| コード | 意味 |
| --- | --- |
| `validation_error` | リクエストボディの形式の誤り、不正な UUID、無効なフィールド、形式が正しくないカーソルのいずれかです。 |
| `limit_reached` | 有効か無効かにかかわらず、API トークンがすでに 10 個あります。先に 1 つ削除してください。 |
| `invalid_workflow` | トークンの `workflowIds` に含まれる値が、個人スペースにあるワークフローではありません。 |
| `token_workspace_mismatch` | 1 つのワークスペースに紐付いたトークンが、別のワークスペースを指定する `X-Nodaro-Workspace` ヘッダーとともに送られました。 |
| `locked_field` | 実行で、外部と通信するノードの送信先または取得元を変更しようとしました。[実行で変更できないもの](https://nodaro.ai/docs/developers/api/workflows#what-a-run-cannot-change)を参照してください。 |
| `image_required` | テキストから動画を作れないモデルで、開始フレームを指定せずに `POST /v1/generate-video` を呼び出しました。代わりにリファレンスを使えるかどうかは、メッセージに示されます。 |
| `sequence_execution_required` | 実行に、スタジオプロダクション API で生成する必要があるスタジオプロダクションのノードが含まれています。 |
| `not_workspace_scoped` | ワークスペースに属していないワークフローの `visibility` を変更しました。 |
| `advanced_mode_unsupported` | 直接呼び出しの経路がないモデルで、`advancedMode: true` を指定しました。 |

### 401 Unauthorized
| コード | 意味 |
| --- | --- |
| `unauthorized` | トークンが指定されていないか、無効、期限切れ、または取り消し済みです。 |

### 402 Payment Required
これらのコードは、Nodaro Cloud と、課金を行うデプロイ環境から返されます。

| コード | 追加フィールド | 意味 |
| --- | --- | --- |
| `insufficient_credits` | `required`、`balance` | アカウントのクレジットが足りません。1 つの請求アカウントが全員分を支払うデプロイ環境では、デプロイ環境のプールが空であることを意味します。その場合、エラーには `required` が含まれますが、`balance` は含まれません。また、クレジットを追加できるのは請求アカウントだけです。 |
| `insufficient_app_credits` | | 公開アプリの実行で、アカウントのクレジットが足りません。 |
| `budget_exceeded` | | ワークスペースが支払う処理で、ワークスペースの予算では実行をまかなえません。ワークスペースの管理者に追加を依頼してください。 |
| `member_cap_exceeded` | | ワークスペースが支払う処理で、そのワークスペースでの自分の利用上限に達しました。 |
| `user_allowance_exceeded` | `required`、`remaining` | ユーザーごとの利用枠を適用している、1 つの請求アカウントが全員分を支払うデプロイ環境で、自分の利用枠では実行をまかなえません。利用枠を引き上げられるのは、請求アカウントだけです。 |
| `instance_cap_reached` | | 接続済みのセルフホスティング環境の OAuth トークンで、その環境が、接続元のアカウントに設定された月間上限を使い切りました。**接続済みのインスタンス**で、上限を引き上げるか解除してください。 |

### 403 Forbidden
| コード | 追加フィールド | 意味 |
| --- | --- | --- |
| `forbidden` | | トークンで扱えるワークフローの範囲にこのワークフローが含まれていないか、このルートにはログイン済みのセッションが必要です。[認証](https://nodaro.ai/docs/developers/api/authentication#routes-that-need-a-signed-in-session)を参照してください。 |
| `insufficient_scope` | `missingScope` | OAuth トークンに、ルートに必要なスコープがありません。より広いスコープを指定して、ユーザーにもう一度同意画面を通ってもらってください。 |
| `in_app_only` | | このルートは、ワークフロー Copilot など、Nodaro の Web アプリ専用です。 |
| `edition_required` | `required_edition` | このルートには、より上位のエディションが必要です。パイプラインの分岐には `cloud`、API トークンの管理には `business` が必要です。 |
| `not_a_member` | | `X-Nodaro-Workspace` ヘッダーが、自分がアクティブなメンバーではないワークスペースを指定しています。 |
| `member_suspended` | | ワークスペースでの自分のメンバーシップが停止されています。 |
| `personal_space_disabled` | | 組織の設定により、ワークスペースの中でしか作成できません。ワークスペースのヘッダーを送ってください。 |
| `project_create_not_allowed` | | プロジェクトを作成できるのは、ワークスペースの管理者だけです。 |
| `workspace_archived` | | ワークフローの共有など、アーカイブ済みのワークスペースへの書き込みです。そこで新たに作成しようとした場合は、代わりに `409` が返ります。 |
| `not_permitted` | | このワークフローを移動する権限がないか、その場所に移動する権限がありません。 |
| `sso_required` | | デプロイ環境がログインを自身の ID プロバイダーに限定しており、このセッションのアカウントは、その ID プロバイダーを通じて作成されていません。トークンには影響しません。 |
| `subscription_required` | | 従量課金のアカウントが、Web エディターからクレジットを使おうとしました。従量課金のクレジットは API、SDK、CLI、MCP で使えるため、トークンでの呼び出しがこのコードを受け取ることはありません。 |
| `api_tokens_payer_only` | | 1 つの請求アカウントが全員分を支払うデプロイ環境では、API トークンを作成できるのは、その請求アカウントだけです。 |
| `payer_balance_jwt_only` | | そのようなデプロイ環境で、請求アカウントがトークンを使ってプールの残高を読み取りました。残高を読み取れるのは、請求アカウント自身のブラウザーセッションだけです。 |
| `payer_required` | | デプロイ環境の請求用のルートが、請求アカウントのブラウザーセッション以外から呼び出されました。 |

### 404 Not Found
| コード | 意味 |
| --- | --- |
| `not_found` | リソースが存在しないか、あなたには見えません。ID から存在の有無がわからないように、どちらの場合も意図的に同じレスポンスを返します。 |
| `workspace_not_found` | ワークスペースが支払う処理で、存在しないワークスペースが指定されました。 |

### 409 Conflict
| コード | 追加フィールド | 意味 |
| --- | --- | --- |
| `already_running` | `executionId` | このワークフローは、すでに実行中です。代わりに、その実行をポーリングしてください。 |
| `workflow_conflict` | `currentVersion`、`currentUpdatedAt`、`currentRecord` | 送ったバージョンの後に、ワークフローが変更されました。`currentRecord` に変更をマージして、もう一度保存してください。 |
| `workspace_archived` | | アーカイブ済みのワークスペースで作成しようとしたか、そこへ移動しようとしました。 |
| `workspace_has_no_default_project` | | ワークスペースでの作成でプロジェクトが指定されておらず、そのワークスペースにはデフォルトのプロジェクトもありません。プロジェクトを指定してください。 |
| `move_blocked` | | 課題のために作成されたものなので、移動できません。 |
| `production_capability_required` | | 汎用のワークフロー保存で、専用の API で扱う必要があるスタジオプロダクションを変更しようとしました。 |
| `retained_image_in_use` | | 削除すると保護された画像が消えてしまうか、プロダクションにまだその画像を使っているジョブがあります。先にそれらのジョブを完了させるか、キャンセルしてください。 |

### 413、422、429
| ステータス | コード | 意味 |
| --- | --- | --- |
| 413 | | アカウントがストレージの上限を超えています。SDK は、`limitBytes` 付きの `StorageExceededError` をスローします。 |
| 422 | `job_blocked` | デプロイ環境のジョブポリシーが、実行前に生成を拒否しました。ジョブは作成されず、何も課金されていません。`message` をそのままユーザーに表示し、同じリクエストを再試行しないでください。 |
| 422 | `upload_blocked` | デプロイ環境のアップロードポリシーが、保存前にファイルを拒否しました。`message` をそのまま表示してください。 |
| 429 | `rate_limited` | 個人用 API トークンの、トークンごとの制限です。 |
| 429 | `rate_limit_exceeded` | それ以外の制限です。アドレスごと、ルートごとの制限や、公開アプリの 1 日あたりの実行回数の制限が該当します。 |
| 429 | `too_many_downloads` | すでに 4 件の動画のインポートが実行中です。 |

`422` のコードは、そのようなポリシーを登録したデプロイ環境でのみ発生します。`429` のコードについては、[レート制限](https://nodaro.ai/docs/developers/api/rate-limits)を参照してください。

### 500、503
| ステータス | コード | 意味 |
| --- | --- | --- |
| 500 | `internal_error` | サーバーエラー、またはサーバーが依存するサービスの障害です。バックオフしながら再試行してください。 |
| 503 | `price_not_configured` | このデプロイ環境では、リクエストしたモデルに料金が設定されていません。誤った金額で課金しないよう、サーバーはリクエストを拒否します。再試行しても解決しません。 |
| 503 | `rate_limit_unavailable` | クレジットを消費するルートが、レート制限を確認できませんでした。しばらくしてから再試行してください。 |
| 503 | `provider_unavailable` | モデルのプロバイダーが利用できません。 |
| 503 | `billing_unavailable` | このインスタンスでは、使用量の報告をまだ利用できません。 |

## ジョブが後から失敗した場合
リクエストが成功しても、作成されたジョブが失敗することがあります。その場合、ジョブには `error_message` が含まれ、2 種類の失敗では、構造化された `error_hint` も含まれます。

- モデルの安全フィルターがリクエストをブロックした場合は、`{ "kind": "safety-block", "class": "copyright" | "likeness" | "safety", "retried": boolean, "suggestedProvider"?: string }` です。
- デプロイ環境のポリシーがリクエストを拒否した場合は、`{ "kind": "policy-block", "policyId": string, "reason": string, "hookPoint": "request" | "result" }` です。

どちらの場合もクレジットは必ず返還され、そのことはジョブの `credit_status` で確認できます。各フィールドの意味と、別のモデルを試すべき場合については、[ジョブが失敗した理由](https://nodaro.ai/docs/developers/api/jobs#why-a-job-failed)を参照してください。

## SDK でのエラー
`@nodaro/sdk` は、失敗したレスポンスごとに型付きのエラーをスローします。最も具体的なクラスから順に捕捉してください。

| クラス | ステータス | 追加フィールド |
| --- | --- | --- |
| `UnauthorizedError` | 401 | |
| `ForbiddenError` | 403 | コードが `insufficient_scope` の場合は `missingScope` |
| `NotFoundError` | 404 | |
| `InsufficientCreditsError` | 402 | `required`、`available` |
| `StorageExceededError` | 413 | `limitBytes` |
| `WorkflowConflictError` | 409 | `currentVersion`、`currentUpdatedAt`、`currentRecord`。スタジオの `production_busy` の競合にも使われます。 |
| `RateLimitedError` | 429 | |
| `JobBlockedError` | 422 | |
| `StudioOpError` | 4xx | `opIndex`：スタジオのバッチのうち、拒否された操作の位置 |
| `NodaroError` | すべて | 基底クラス：`code`、`status`、`message` |

```ts

NodaroError,
ForbiddenError,
InsufficientCreditsError,
RateLimitedError,
} from '@nodaro/sdk'

try {
await client.workflows.run(workflowId)
} catch (err) {
if (err instanceof InsufficientCreditsError) {
showPaywall({ required: err.required, available: err.available })
} else if (err instanceof ForbiddenError && err.missingScope) {
requestConsent([err.missingScope])
} else if (err instanceof RateLimitedError) {
await new Promise((r) => setTimeout(r, 5_000))
} else if (err instanceof NodaroError) {
console.error(`API error ${err.status} (${err.code}): ${err.message}`)
} else {
throw err // a network failure, not an API error
}
}
```

ポーリング用のヘルパーは、`JobFailedError` や `JobTimeoutError` など、独自のエラーも追加します。[単体のノードを実行する](https://nodaro.ai/docs/developers/api/nodes#example-generate-an-image)を参照してください。

## CLI でのエラー
| 終了コード | 意味 |
| --- | --- |
| `0` | 成功です。 |
| `1` | 認証の失敗、リソースが見つからない、引数の誤り、ネットワークエラーのいずれかです。 |
| `2` | `--watch` が終了し、実行は失敗しました。 |
| `3` | ジョブがレビューのために保留されたため、`--watch` が停止しました。失敗ではありません。後で確認してください。 |
| `130` | `--watch` が終了し、実行はキャンセルされました。 |

`--json` を付けると、CLI はコード 2、3、130 を使わずに、ペイロードを出力して正常終了します。そのため、`.status` を自分で確認してください。

## Frequently asked questions

### Nodaro API のエラーは、どのような形式ですか？

すべてのエラーは、HTTP ステータスと、{ "error": { "code": "…", "message": "…" } } という本文を返します。code は変更されないので、処理の分岐には code を使います。message は人が読むためのもので、変わることがあります。

### Nodaro API のどのエラーを再試行すればよいですか？

5xx エラーと 429 は、指数バックオフで再試行します。たとえば、5 秒後、10 秒後、20 秒後です。4xx エラーは、同じリクエストが再び失敗するので、リクエストを変更せずに再試行しないでください。サーバーエラーのうち、503 price_not_configured は例外です。

### 402 insufficient_credits は、どういう意味ですか？

アカウントに、実行を開始するのに十分なクレジットがありません。エラーには、実行に必要なクレジットを示す required と、通常は balance が含まれます。Nodaro Cloud でクレジットを追加してから、リクエストをもう一度送ってください。

### 失敗したジョブの error_hint とは何ですか？

モデルの安全フィルターとデプロイ環境のポリシーという 2 種類の失敗について、失敗したジョブに付く構造化された理由です。error_message を解析する代わりに、この値を読み取ってください。たとえば、提案されたモデルをユーザーに示すときに使います。

### ルートが 403 in_app_only を返すのはなぜですか？

そのルートが、Nodaro の Web アプリ自身のセッション専用だからです。たとえば、ワークフロー Copilot や、Webhook 出力（Webhook Output）ノードの保存済みの認証情報がそうです。API トークンや OAuth トークンでは呼び出せません。代わりに、ワークフローのエンドポイント、SDK、MCP を使ってください。
