Seedance のエラーは二層に分かれます。一つは HTTP リクエストの失敗で、401、403、400、429 などです。もう一つはリクエストは成功したものの、タスクが最終的に failed または expired になるものです。送信インターフェースが 200 を返しただけで成功と判断するのは、動画 API を接続するときの最もよくある誤判定です。正しい切り分けの順序は、まず HTTP ステータスコードを見て、次にタスク状態と error フィールドを見て、最後にプロンプトを変更するかモデルを替えることです。

本記事ではエラーの種類ごとに、原因、処理順序、再利用可能なエラー処理コードを示します。API パス、状態名、枠の基準はバージョンによって変わる場合があるため、実際にはサイト内ドキュメントと公式ドキュメントに従ってください。

注記:Seedance、豆包、火山エンジンは字節跳動およびその関連会社の商標です。本記事は技術的な説明のみを目的とし、資本関係や推奨関係を示すものではありません。例に含まれるパスとフィールドはデモ用であり、呼び出し前にドキュメントを確認してください。

一、まずリクエスト失敗とタスク失敗を分ける

現象 層 よくある原因 処理
401 HTTP Key の欠落、形式エラー、失効 認証を修正
403 HTTP 権限不足、モデル未開通、地域制限 権限と開通状態を確認
400 HTTP パラメータの範囲外、モデル名の誤り、素材にアクセス不可 ドキュメントに照らして修正
429 HTTP 同時実行、レート、割当の制限 バックオフ、同時実行を下げる
5xx HTTP 上流の過負荷または一時障害 バックオフ、経路を切り替え
failed タスク 審査拒否、素材の問題、生成異常 error を読み、素材を修正
expired タスク 時間内に完了せず、結果も取得されず タスクを再作成、期限を確認
ダウンロード失敗 結果 URL の期限切れ、認証、ネットワーク 再取得または転送保存

ログには少なくとも時刻、HTTP ステータスコード、エラーコード、モデル名、task_id、業務注文番号、所要時間を記録します。完全な Key を記録せず、署名付きの結果 URL をそのまま出力しないでください。

二、401 と 403:まずリクエストヘッダーと Key を確認する

401 は身元の検証に失敗したことを示し、通常はモデルの能力や残高とは関係ありません。順番に確認します。

  • Authorization が Bearer <key> か。火山方舟は API Key を Bearer Token として使う。
  • Key に空白、改行、余分な引用符が付いていないか。環境変数が実際に読み込まれているか。
  • Base URL と Key が同じプラットフォーム、同じリージョンに属しているか。
  • Key が削除、ローテーション、停止されていないか。プロジェクトの権限が不足していないか。
  • リバースプロキシが Authorization ヘッダーを上書きまたは破棄していないか。

403 はより権限の問題です。モデル未開通、アカウントに該当モデルの権限がない、地域またはコンプライアンスの制限が考えられます。401 は新しい Key で最小リクエストを行えばすぐに特定できます。403 はコンソールの開通状態とアカウント権限を確認する必要があり、コードを変更しても通常は解決しません。

三、429:レート制限、同時実行、割当

429 はリクエストがレート、同時実行、割当の制限を超えたことを示します。よくある発生場面は次のとおりです。

  • 複数サービスが一つの Key を共有し、枠を奪い合っている。
  • バッチタスクが毎時ちょうどに同時起動し、流量パルスを形成している。
  • 再試行にバックオフがなく、失敗するほど送信して再試行の嵐になっている。
  • 長いタスクが同時実行を占有し、短いタスクが入れない。

処理の原則は、指数バックオフとランダムジッター、同時実行の削減、プロジェクトごとの Key 分割、送信時間の分散です。バックオフなしでループ再試行しないでください。429 の間に高頻度で送信し続けると、レート制限が持続的な障害に変わります。業務で本当に高い同時実行が必要な場合は、事前に割当上限を確認し、代替経路を用意してください。

四、400:パラメータエラーが大多数

400 は通常、エラーメッセージから直接特定できます。重点的に確認する項目は次のとおりです。

  • モデル名とバージョン:ドキュメントからコピーし、記憶で接尾辞を組み立てない。
  • 長さ:モデルごとに範囲が異なる。たとえば 2.5 は 4〜30 秒、2.0 は 4〜15 秒、1.0 は 2〜12 秒。
  • 解像度と比率:そのモデルが 4K、21:9、adaptive に対応しているか確認する。
  • 参照素材の数:2.5 は最大 30 枚の画像、10 本の動画、10 本の音声。2.0 の上限はより低い。
  • 画像または動画の URL:プラットフォームからアクセスできる必要があり、社内網のアドレスやログインが必要なリンクは不可。
  • 編集または続き生成のタスク:一部のモードは自動長さのみで、秒数を手入力できない。
  • 音声入力:一部のバージョンは音声のみの入力に対応していない。

最も時間を節約できる方法は、ドキュメントのサンプルリクエスト本文で最小再現を行い、そこに自分のパラメータを一つずつ戻していくことです。一つ追加するたびにテストすれば、具体的なフィールドを特定できます。

五、タスクの failed と expired

送信成功はタスクがキューに入ったことだけを意味します。結果照会でよく見る状態は queued、running、succeeded、failed、expired です。failed は error フィールドを見ます。内容審査の拒否、入力素材の不適合、参照画像の読み取り不能、生成過程の異常で、対処法はまったく異なります。

expired は、タスクが実行期限を超えても完了せず、結果も取得されない場合によく起こります。ドキュメントでは execution_expires_after を設定でき、既定は 48 時間です。タスク ID は 7 日間保持されます。期限を過ぎてからポーリングを続けても回復せず、タスクを再作成するしかありません。コールバックを使う場合は、5 秒以内に確認応答しないと 3 回再試行されるため、コールバックインターフェースは冪等でなければなりません。

審査拒否は、いくつかの言葉を変えて繰り返し回避しようとしないでください。まず素材の権利、人物の肖像、ブランド要素、コンテンツポリシーを確認し、それから再提出するか決めます。

六、タイムアウトとポーリング

タイムアウトは三層に分けます。クライアントの接続タイムアウト、単回照会のタイムアウト、タスク全体の deadline です。送信インターフェースは通常すぐに task_id を返し、実際に時間がかかるのは待ち行列とレンダリングです。HTTP クライアントのタイムアウトが短すぎると、正常な送信を失敗と誤判定します。長すぎるとスレッドプールを占有します。

ポーリングは 5 秒から始め、徐々に 15〜30 秒までバックオフし、全体の deadline を設定することをおすすめします。コールバックに対応している場合はコールバックを優先し、ポーリングはフォールバックとします。タイムアウト後に直ちに再送信せず、まず業務注文番号または冪等キーで既存タスクが動いていないか確認してください。

七、結果ダウンロードの失敗

タスク成功はファイル入手と同じではありません。video_url は署名付きの一時アドレスであることが多く、有効期限があります。そのままデータベースに保存し、数時間後にダウンロードすると、403 や 404 がよく発生します。対処法は次のとおりです。

  • 成功後すぐにストリーミングで自社のオブジェクトストレージにダウンロードする。
  • ダウンロード失敗は別に再試行し、生成タスクを再作成しない。
  • Content-Type、ファイルサイズ、再生可能性を検証する。
  • ログに完全な署名付き URL を出力しない。

八、例:バックオフと冪等性を備えたエラー処理

次のコードは送信、ポーリング、ダウンロードを分けて処理し、一時的なエラーのみを再試行します。パスとフィールドはサイト内ドキュメントを基準とします。

import os, time, random, requests

BASE = os.environ["SEEDANCE_BASE_URL"].rstrip("/")
HEADERS = {
    "Authorization": f"Bearer {os.environ['SEEDANCE_API_KEY']}",
    "Content-Type": "application/json",
}

def submit(payload, max_retries=5):
    # 呼び出し前に業務注文番号でタスク表を確認し、既存の task_id があれば再利用する
    for attempt in range(max_retries):
        try:
            r = requests.post(
                f"{BASE}/contents/generations/tasks",
                headers=HEADERS, json=payload, timeout=(5, 30),
            )
        except requests.Timeout:
            time.sleep(min(2 ** attempt, 20) + random.random())
            continue
        if r.status_code == 429 or r.status_code >= 500:
            time.sleep(min(2 ** attempt, 20) + random.random())
            continue
        if r.status_code >= 400:
            raise RuntimeError(f"{r.status_code}: {r.text[:500]}")
        return r.json()["id"]
    raise RuntimeError("submit retries exhausted")

def wait(task_id, deadline_seconds=900):
    deadline = time.time() + deadline_seconds
    delay = 5
    while time.time() < deadline:
        r = requests.get(
            f"{BASE}/contents/generations/tasks/{task_id}",
            headers=HEADERS, timeout=(5, 30),
        )
        if r.status_code == 429 or r.status_code >= 500:
            time.sleep(delay)
            delay = min(delay * 1.5, 30)
            continue
        r.raise_for_status()
        data = r.json()
        if data["status"] == "succeeded":
            return data["content"]["video_url"]
        if data["status"] in ("failed", "expired"):
            raise RuntimeError(f"{data['status']}: {data.get('error')}")
        time.sleep(delay)
        delay = min(delay * 1.5, 30)
    raise TimeoutError(task_id)

def download(url, path):
    with requests.get(url, stream=True, timeout=(5, 120)) as r:
        r.raise_for_status()
        with open(path, "wb") as f:
            for chunk in r.iter_content(1024 * 256):
                f.write(chunk)

このコードは意図的に 400、401、403 を再試行しません。パラメータエラーと認証エラーは何度再試行しても成功せず、ログと費用を増やすだけです。

九、再試行、冪等性、サポート用チェックリスト

再試行が適するのは一時的なエラーだけです。429、5xx、接続タイムアウトです。400、401、403、審査拒否は自動再試行すべきではありません。冪等性の鍵は再試行回数ではなく業務注文番号です。送信前にタスク表を確認し、送信後に task_id を保存し、重複リクエストは既存タスクを再利用します。

サービス提供者に連絡する前に、次のチェックリストで情報を整理します。

  1. 発生日時、HTTP ステータスコード、エラーコード、完全なエラーメッセージを記録する。マスキングに注意。
  2. モデル名、長さ、解像度、比率、参照素材の数を記録する。
  3. task_id、業務注文番号、送信回数、ポーリング回数を記録する。
  4. 最小再現リクエストを一件保存し、ユーザーのプライバシーと鍵を除去する。
  5. リクエスト失敗とタスクの failed または expired を区別する。
  6. 素材 URL が公開アクセス可能か、応答が遅すぎないか確認する。
  7. 割当、同時実行、アカウント権限を確認し、429 と 403 を排除する。
  8. リクエスト ID と再現時刻を添えてから調査を依頼する。

よくある質問

送信インターフェースが 200 を返したのに、タスクが失敗するのはなぜですか?

200 はタスクの作成成功だけを意味します。動画生成は非同期なので、最終状態は照会結果の status と error フィールドで確認します。

429 は残高不足ですか?

いいえ、とは限りません。429 は通常、レート、同時実行、割当の制限を示し、残高の問題は別のエラーを返す場合があります。まず同時実行、Key の共有、再試行戦略を確認してください。

タスクが failed になった後、そのまま再試行できますか?

まず error を読みます。審査拒否や素材の不適合は、そのまま再試行しても通常は再度失敗します。ネットワークや上流の障害はバックオフして再試行できます。再試行前に業務注文番号で冪等性を確認してください。

ポーリングの間隔はどのくらいが適切ですか?

5 秒から始め、徐々に 15〜30 秒までバックオフし、全体のタイムアウトを設定します。コールバックに対応している場合はコールバックを優先し、ポーリングはフォールバックとします。

結果 URL が開けない場合は?

多くは有効期限切れまたは署名の失効です。成功後すぐにオブジェクトストレージへ転送保存してください。すでに期限切れの場合は、タスクを再照会するか再作成し、古いリンクを繰り返し要求しないでください。

次のステップ

まずZSQH AI Gatewayでテスト用 Key を作成し、401、429、パラメータエラー、タスク失敗をそれぞれ一度ずつ演習して、アラートと再試行戦略が有効か確認してください。モデルの可用性と課金方式はモデル価格を基準とします。

関連記事: