エラー
Nodaro の TypeScript SDK がスローするすべてのエラーを、HTTP ステータス、コード、フィールドとともに説明し、クレジット、レート制限、競合、失敗したジョブへの対処法も扱います。
Nodaro SDK が API の応答に対してスローするエラーは、すべて NodaroError かそのサブクラスのインスタンスです。エラーステータスで失敗したリクエストは、そのステータスに対応するサブクラスをスローします。run-and-wait のヘルパーは、ジョブが失敗、タイムアウト、停止したときに、それぞれ専用のサブクラスをスローします。具体的なクラスから先にキャッチし、NodaroError は最後にキャッチしてください。
すべてのエラークラス
| クラス | ステータス | code | 追加のフィールド | スローされる条件 |
|---|---|---|---|---|
NodaroError | いずれか | サーバー自身のコードです | 基底クラスで、より具体的なクラスがないすべてのエラーです | |
UnauthorizedError | 401 | unauthorized | トークンが未指定か、期限切れか、無効です | |
ForbiddenError | 403 | forbidden | missingScope? | 権限がない、または OAuth トークンにスコープが不足しています |
NotFoundError | 404 | not_found | 項目が存在しない、またはあなたには見えません | |
RateLimitedError | 429 | rate_limited | リクエストが多すぎます | |
InsufficientCreditsError | 402 | insufficient_credits | required?、available? | アカウントが、実行の料金を払えません |
StorageExceededError | 413 | storage_exceeded | limitBytes? | アカウントのストレージがいっぱいです |
WorkflowConflictError | 409 | workflow_conflict または production_busy | currentUpdatedAt?、currentVersion?、currentRecord? | 先に、ほかの人がその項目を変更しました |
JobBlockedError | 422 | job_blocked | デプロイ環境のコンテンツポリシーが、リクエストを拒否しました | |
StudioOpError | 4xx | サーバー自身のコードです | opIndex | スタジオのバッチ内の 1 つの操作が、拒否されました |
JobFailedError | 0 | job_failed | jobId、jobStatus | 待っていたジョブが、失敗またはキャンセルされました |
JobTimeoutError | 0 | job_timeout | jobId、timeoutMs | ジョブが maxMs 以内に終わりませんでした |
JobAbortedError | 0 | job_aborted | jobId? | 待っている間に、自分の AbortSignal が発火しました |
JobHeldError | 0 | job_held | jobId | ジョブが、人によるレビューのために保留されています |
StudioPreviewUnavailable | 0 | studio_preview_unavailable | デプロイ環境が、スタジオのバッチをプレビューできません | |
StudioPreviewAppliedError | 0 | studio_preview_applied | applied | プレビューを求めたスタジオのバッチが、適用されました |
どのクラスにも、3 つのフィールドがあります。読みやすい文である message、比較できる安定した文字列である code、HTTP ステータスである status です。status が 0 の場合、そのエラーは HTTP の応答から来たものではないことを意味します。たとえば JobTimeoutError は、SDK 自身のポーリングループがスローします。
一部のステータスは、サーバーが送ったコードにかかわらず、1 つのクラスに対応づけられます。403 は、code が forbidden に設定された ForbiddenError になり、404 は NotFoundError になります。これらの場合、サーバーの理由は message を読んでください。400、409、503 など、それ以外のステータスのエラーは、サーバー自身の code を持つ NodaroError として届きます。
エラーを順番にキャッチする
import {
ForbiddenError,
InsufficientCreditsError,
NodaroError,
NotFoundError,
RateLimitedError,
StorageExceededError,
UnauthorizedError,
} from "@nodaro/sdk"
try {
await client.workflows.run(workflowId)
} catch (err) {
if (err instanceof UnauthorizedError) {
redirectToLogin()
} else if (err instanceof ForbiddenError) {
if (err.missingScope) requestAdditionalScopes([err.missingScope])
else showError("You do not have permission to do this.")
} else if (err instanceof InsufficientCreditsError) {
showCreditPaywall({ required: err.required, available: err.available })
} else if (err instanceof RateLimitedError) {
await retryWithBackoff()
} else if (err instanceof StorageExceededError) {
showError(`Storage limit of ${err.limitBytes} bytes reached.`)
} else if (err instanceof NotFoundError) {
showError("Not found.")
} else if (err instanceof NodaroError) {
console.error(`API error ${err.status} (${err.code}): ${err.message}`)
} else {
throw err // a network failure or a timeout, not an API answer
}
}クライアントの timeoutMs を超えたリクエストと、ネットワークの障害は、どちらもランタイム自身のエラー(AbortError や TypeError など)を伴って失敗します。これらは NodaroError のインスタンスではありません。
認証と権限
UnauthorizedError
HTTP 401 です。トークンが未指定か、期限切れか、無効です。新しいトークンを取得するか、ユーザーにもう一度ログインしてもらってから、再試行してください。
ForbiddenError
HTTP 403 です。呼び出し元に、この操作を行う権限がありません。OAuth トークンに、エンドポイントが必要とするスコープが許可されていない場合、missingScope にそのスコープの名前が入ります。たとえば workflows:execute です。ユーザーにそのスコープを承認してもらい、新しいトークンで再試行してください。スコープと権限の不足を参照してください。
ほかの理由には、その機能を提供していないエディションであることや、ロールの権限が低すぎることがあります。いずれも code は forbidden になるため、どちらなのかは message を表示して説明してください。
NotFoundError
HTTP 404 です。項目が存在しない、またはこの呼び出し元には見えないことを意味します。Nodaro はどちらの場合も同じように応答するため、ID によって、見えないものが存在するかどうかがわかることはありません。スタジオプロダクションや Recast のように、Nodaro Cloud にしか存在しないリソースも、セルフホスティング環境では 404 を返します。
クレジット、ストレージ、上限
InsufficientCreditsError
HTTP 402 です。アカウントが実行の料金を払えないため、何も開始されませんでした。required は実行に必要なクレジットの数、available はアカウントが持つクレジットの数です。どちらも Nodaro Cloud が設定しますが、型としては省略可能です。
try {
await client.nodes.runAndWait("generate-video", params)
} catch (err) {
if (err instanceof InsufficientCreditsError) {
console.log(`Need ${err.required} credits, have ${err.available}`)
}
}実行の前に、client.credits.balance() で残高を確認してください。クレジットを参照してください。
StorageExceededError
HTTP 413 です。アカウントがストレージの上限に達したことを示し、limitBytes がその上限です。アップロードや、コミュニティの複製のようにストレージへのコピーを行う処理で、このエラーがスローされます。不要なメディアを削除してから、再試行してください。
RateLimitedError
HTTP 429 です。リクエストを送りすぎました。待ってから、間隔を広げながら再試行してください。たとえば 2 秒、次に 4 秒、次に 8 秒です。何度か試したら止めてください。レート制限を参照してください。
ジョブを待つ
client.nodes.runAndWait()、client.nodes.runMany()、そしてほかのリソースの ...AndWait ヘルパーは、ジョブが終わるまでポーリングします。待っている間、これらは次のエラーをスローします。
JobFailedError
ジョブが failed または cancelled のステータスで終わりました。どちらであるかは jobStatus が示し、jobId はそのジョブを、message はジョブ自身のエラーメッセージを示します。client.jobs.get(err.jobId) でジョブ全体を読み取ってください。その error_hint が、安全フィルターやポリシーによるブロックを説明します。
JobTimeoutError
ジョブが maxMs(デフォルトは 15 分)以内に、終了状態に達しませんでした。ジョブはキャンセルされません。たいていはそのままサーバー上で完了し、あなたのライブラリに届きます。後から client.jobs.get(err.jobId) で取得するか、遅いモデルには、より大きな maxMs を渡してください。プラットフォームが復旧中のジョブは、そのステータスに recovering: true が示され、復旧には数十分かかることがあります。
JobAbortedError
自分の AbortSignal が発火しました。SDK はただちにポーリングを止めます。ジョブはキャンセルされません。サーバー上でジョブを止め、確保されていたクレジットを返還するには、client.jobs.cancel(err.jobId) を呼び出してください。
JobHeldError
ジョブが pending_review のステータスに達しました。このデプロイ環境のコンテンツポリシーが、人によるレビューのために結果を保留したことを意味します。SDK は、このステータスを最初に確認したポーリングで、待機を止めます。ジョブはキャンセルされず、レビューの間、クレジットは確保されたままです。リクエストをもう一度実行しないでください。重複したジョブも、同じように保留されるためです。
後から client.jobs.get(err.jobId) でジョブを確認してください。レビュー担当者が承認すると completed に、却下すると failed になり、あなたがキャンセルすると cancelled になります。却下されたジョブには、policy-block に設定された error_hint.kind と、そのまま表示できる reason が含まれます。このエラーが起きるのは、ジョブのポリシーを登録しているデプロイ環境だけです。
JobBlockedError
HTTP 422 で、コードは job_blocked です。このデプロイ環境のコンテンツポリシーが、実行される前にリクエストを拒否しました。ジョブは作成されず、料金も発生していません。message はユーザー向けに書かれているため、そのまま表示してください。同じリクエストを再試行しないでください。このエラーが起きるのは、ジョブのポリシーを登録しているデプロイ環境だけです。
同時に行われた変更
WorkflowConflictError
HTTP 409 です。条件付きの変更を行おうとしたところ、先にほかの人がその項目を変更していました。次の 2 つのコードのいずれかで届きます。
workflow_conflict:expectedVersionまたはexpectedUpdatedAtを指定したclient.workflows.update()が、保存されているワークフローと一致しませんでした。production_busy:サーバーがあなたの変更を適用している間も、スタジオプロダクションが変化し続けたため、サーバーが再試行を止めました。
どちらも対処法は同じです。項目をもう一度読み取り、最新のコピーに変更を適用して、もう一度送信してください。サーバーが含めている場合、currentRecord に現在のワークフローが入っているため、もう一度読み取らずにマージできます。
import { WorkflowConflictError } from "@nodaro/sdk"
try {
await client.workflows.update(id, { settings, expectedVersion: loadedVersion })
} catch (err) {
if (err instanceof WorkflowConflictError && err.currentRecord) {
const merged = mergeSettings(err.currentRecord.settings, settings)
await client.workflows.update(id, { settings: merged, expectedVersion: err.currentVersion })
} else {
throw err
}
}ロケーションとオブジェクトは、独自の競合コードである concurrent_modification を使い、これは通常の NodaroError として届きます。対処法は同じです。項目をもう一度読み取り、マージしてから再試行してください。
スタジオのバッチ
この 3 つのクラスは、スタジオプロダクションに関するものです。
StudioOpError:操作のバッチが拒否され、opIndexにはその原因となった操作の位置が、0 から数えて入ります。バッチ内のものは何も書き込まれていません。その操作を修正して、バッチ全体をもう一度送信してください。StudioPreviewUnavailable:dryRun: trueを付けてプレビューを求めたものの、このデプロイ環境ではプレビューを提供できません。バッチは送信されていません。プレビューが利用できないことをユーザーに伝え、確認なしにバッチを適用しないでください。StudioPreviewAppliedError:プレビューを求めたにもかかわらず、バッチが適用されてしまいました。applied.productionとapplied.versionを、現在の状態として扱ってください。バッチをもう一度送信しないでください。appliedがundefinedの場合は、何かを決める前に、プロダクションをもう一度読み取ってください。
NodaroError で見られるコード
これらのコードは、通常の NodaroError に付いて届きます。err.code を比較して処理してください。
| ステータス | コード | 対象 | 意味 |
|---|---|---|---|
| 400 | validation_error | 多くのメソッド | フィールドが不足しているか、無効です。message にその名前が示されます。 |
| 400 | no_valid_inputs | client.reduce.run() | すべての入力が空でした。 |
| 400 | invalid_edl | client.edit.applyEdl() | 編集決定リストが、検証に失敗しました。 |
| 400 | limit_reached | client.developerApps.create() | すでにアプリの上限数に達しています。 |
| 400 | locked_field | client.apps.run() | オーバーライドが、Webhook の URL など、送信先を変更しようとしました。 |
| 409 | name_taken | キャラクター、組織 | 名前またはスラッグが、すでに使われています。 |
| 409 | concurrent_modification | ロケーション、オブジェクト | 読み取った後に、項目が変更されました。 |
| 410 | voice_cloning_retired | client.voices.createClone() | ボイスクローニングは、もう提供されていません。代わりにボイスデザインを使ってください。 |
| 503 | provider_unavailable | client.llm.structuredJob() | このインスタンスでは、このモデルを実行できません。再試行しないでください。 |
| 503 | feature_disabled | client.copilot | この機能は、このデプロイ環境ではオフになっています。 |
| 503 | nodaro_connection_required | client.edit.editPlan() | セルフホスティング環境でこれを使うには、Nodaro Cloud への接続が必要です。 |
各リファレンスページには、そのページ自身のメソッドのコードが一覧表示されています。REST API のエラーページには、API が送るすべてのコードが一覧表示されています。
安全に再試行する
- 読み取りは、自由に再試行できます。
getやlistには、副作用がありません。 - 有料のリクエストを、むやみに繰り返さないでください。タイムアウトしたリクエストでも、実行はすでに始まっている場合があります。生成を再試行する前に、冪等性キーを渡してください。
client.nodes.run(type, params, { idempotencyKey })とrunAndWaitは、これを受け付けます。同じリクエストを再試行するときに同じキーを使うと、プラットフォームは 2 回目の実行を開始して課金する代わりに、最初の実行を返します。 - スタジオと Recast は、独自の再試行用トークンを使います。スタジオのメソッドでは
clientRequestId、client.recast.rescore()ではrequestIdです。 - 5xx の応答は、間を置いて再試行してください。
createClient内でのカスタムfetchは、そのための処理を置くのに適した場所です。
throwFromResponse(status, body)
throwFromResponse(status: number, body: {
error?: { code?: string; message?: string; [key: string]: unknown }
}): neverHTTP ステータスと Nodaro のエラーボディを、対応するエラークラスに変換してスローします。SDK はすべての応答でこれを使っており、クライアントを使わずに API を呼び出すカスタムのトランスポートのためにも、これはエクスポートされています。
Prop
Type
import { throwFromResponse } from "@nodaro/sdk"
throwFromResponse(403, {
error: { code: "insufficient_scope", message: "Missing scope", missingScope: "workflows:execute" },
})
// throws a ForbiddenError whose missingScope is "workflows:execute"よくある質問
関連ページ
TypeScript SDK
ジョブと実行
ノードの実行
エラー
レート制限
最終更新