Nodaro ドキュメント
ドキュメントノードリファレンスモデルAI エージェント(MCP)開発者向けセルフホスティングリサーチ
TypeScript SDK

エラー

Nodaro の TypeScript SDK がスローするすべてのエラーを、HTTP ステータス、コード、フィールドとともに説明し、クレジット、レート制限、競合、失敗したジョブへの対処法も扱います。

Nodaro SDK が API の応答に対してスローするエラーは、すべて NodaroError かそのサブクラスのインスタンスです。エラーステータスで失敗したリクエストは、そのステータスに対応するサブクラスをスローします。run-and-wait のヘルパーは、ジョブが失敗、タイムアウト、停止したときに、それぞれ専用のサブクラスをスローします。具体的なクラスから先にキャッチし、NodaroError は最後にキャッチしてください。

すべてのエラークラス

クラスステータスcode追加のフィールドスローされる条件
NodaroErrorいずれかサーバー自身のコードです基底クラスで、より具体的なクラスがないすべてのエラーです
UnauthorizedError401unauthorizedトークンが未指定か、期限切れか、無効です
ForbiddenError403forbiddenmissingScope?権限がない、または OAuth トークンにスコープが不足しています
NotFoundError404not_found項目が存在しない、またはあなたには見えません
RateLimitedError429rate_limitedリクエストが多すぎます
InsufficientCreditsError402insufficient_creditsrequired?、available?アカウントが、実行の料金を払えません
StorageExceededError413storage_exceededlimitBytes?アカウントのストレージがいっぱいです
WorkflowConflictError409workflow_conflict または production_busycurrentUpdatedAt?、currentVersion?、currentRecord?先に、ほかの人がその項目を変更しました
JobBlockedError422job_blockedデプロイ環境のコンテンツポリシーが、リクエストを拒否しました
StudioOpError4xxサーバー自身のコードですopIndexスタジオのバッチ内の 1 つの操作が、拒否されました
JobFailedError0job_failedjobId、jobStatus待っていたジョブが、失敗またはキャンセルされました
JobTimeoutError0job_timeoutjobId、timeoutMsジョブが maxMs 以内に終わりませんでした
JobAbortedError0job_abortedjobId?待っている間に、自分の AbortSignal が発火しました
JobHeldError0job_heldjobIdジョブが、人によるレビューのために保留されています
StudioPreviewUnavailable0studio_preview_unavailableデプロイ環境が、スタジオのバッチをプレビューできません
StudioPreviewAppliedError0studio_preview_appliedappliedプレビューを求めたスタジオのバッチが、適用されました

どのクラスにも、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 を比較して処理してください。

ステータスコード対象意味
400validation_error多くのメソッドフィールドが不足しているか、無効です。message にその名前が示されます。
400no_valid_inputsclient.reduce.run()すべての入力が空でした。
400invalid_edlclient.edit.applyEdl()編集決定リストが、検証に失敗しました。
400limit_reachedclient.developerApps.create()すでにアプリの上限数に達しています。
400locked_fieldclient.apps.run()オーバーライドが、Webhook の URL など、送信先を変更しようとしました。
409name_takenキャラクター、組織名前またはスラッグが、すでに使われています。
409concurrent_modificationロケーション、オブジェクト読み取った後に、項目が変更されました。
410voice_cloning_retiredclient.voices.createClone()ボイスクローニングは、もう提供されていません。代わりにボイスデザインを使ってください。
503provider_unavailableclient.llm.structuredJob()このインスタンスでは、このモデルを実行できません。再試行しないでください。
503feature_disabledclient.copilotこの機能は、このデプロイ環境ではオフになっています。
503nodaro_connection_requiredclient.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 }
}): never

HTTP ステータスと 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"

よくある質問

最終更新

目次