Skip to content

Claude Message Threads は API ゲートウェイ経由だとどこで壊れるか ​

最終確認:2026-11-03。Anthropic の API リリースノートと Message threads のドキュメントで確認しました。機能はベータ版で、フィールドや挙動は変わる可能性があります。実装の前に必ず公式ドキュメントを確認してください。

結論

2026-11-02、Anthropic は Messages API で Message Threads のベータ提供を始めました(ベータヘッダー message-threads-2026-08-12)。最初に会話全体を一度送れば、以降のターンでは新しいメッセージと直前の応答の id だけを送り、会話はサーバー側で組み立て直されます。Anthropic を直接呼ぶ場合は扱いやすい機能ですが、ゲートウェイやプロキシには次の 3 つの厳しい制約があります。

  • 1 つの thread のリクエストは、すべて同じキー・同じ workspace に届かなければなりません。別のキーに届いた continue リクエストは 404 になります。
  • リクエストボディの thread フィールドはそのまま転送しなければなりません。落としてもエラーにはならず、モデルがそれまでの会話なしに回答するだけです。
  • model、thinking、anthropic-beta の値の組み合わせなどの「固定フィールド」は、thread の途中で変えられません。

以下、それぞれの制約と、ゲートウェイ側・クライアント側の対処を説明します。

30 秒でわかる Message Threads ​

ステップ送るものポイント
作成会話全体、system、tools に "thread": {"type": "create"} を加える応答を含めた会話が、応答の id の下に保存される
継続直前の応答以降の新しいメッセージだけに "thread": {"type": "continue", "previous_message_id": "<直前の id>"} を加えるsystem と tools は送らない(null でも送ると 400)
再送404 thread_not_found が返ったら、手元に保存した会話全体で作成リクエストを送り直すthread はベストエフォート。最後のリクエストから 5 分で期限切れになり、遅くとも 1 時間で削除される

Anthropic は、長い会話やエージェントループでリクエストを小さくし、最初のトークンまでの時間を短くするための機能と位置づけています。毎ターン会話全体をアップロードし直し、検証とトークン化をやり直す処理がなくなるためです。誤解しやすい点が 3 つあります。

  • 会話の正本はアプリ側にあります。 ドキュメントは、すべてのメッセージのコピーを手元に持ち、再送の経路を最初から実装しておくよう求めています。どの応答も、そのターンが保存されたかどうかを教えてくれません。わかるのは次の continue リクエストのときです。
  • thread はキャッシュではありません。 ドキュメントによれば、thread はプロンプトをキャッシュしません。キャッシュブレークポイントの位置は引き続き自分で決めます。詳しくは「thread とプロンプトキャッシュ」を参照してください。
  • 料金についてドキュメントに個別の記載はありません。 この記事では「thread で安くなるか」は判断しません。各応答の usage を基準にしてください。

また、Anthropic の開発者向けアカウント @ClaudeDevs は X で、Claude Code がすでに thread を使っていると投稿し、長いコンテキストのリクエストで最初のトークンまでの時間が大きく短縮したとする数字も示しています。2026-11-03 時点のドキュメントには該当する記載がないため、単一ソースの情報として扱ってください。一方、SDK のベータ版 tool runner がデフォルトで thread を使うことはドキュメントに明記されています(作成時に無効化できます)。自分で有効にした覚えがなくても、リクエストに thread が含まれている可能性があります。

フィールドは 3 種類 ​

ゲートウェイの問題を調べるときは、まずこの表を見てください。ドキュメントの "What to send on each request" をまとめたものです。

種類フィールドルール
作成時のみsystem、tools作成リクエストでだけ送る。continue リクエストに含めると 400
固定model、tool_choice、thinking、mcp_servers(各サーバーの認可トークンを除く)、context_management、output_config、container 内の skills、トップレベルの cache_control、anthropic-beta の値の組み合わせ毎回、作成時と同じ値で送る。値が違うと 400 thread_fingerprint_mismatch になり、エラーメッセージに違うフィールド名が出る
自由max_tokens、stop_sequences、stream、metadata、service_tier などターンごとに変えてよい

ゲートウェイが thread を壊す 7 つのパターン ​

公式 SDK の thread ヘルパーで Anthropic を直接呼んでいるなら、気にすることはほとんどありません。問題は「クライアント → ゲートウェイ → 上流」の経路で起きます。ゲートウェイが負荷分散、フェイルオーバー、課金のためにふだん行っている処理が、thread の仕組みとぶつかるのです。

#ゲートウェイのふだんの処理thread への影響クライアントから見える症状
1複数の上流キーや workspace への負荷分散continue リクエストが別のキーに届く。ドキュメントは「別の organization、workspace、ユーザー、サービスアカウントからのリクエスト」を 404 の原因に挙げている404 thread_not_found が頻発する。SDK が再送して呼び出し自体は成功するが、毎ターン会話全体を送り直すので thread の意味がない
2ボディを固定の構造体に解析して再シリアライズする構造体に thread フィールドがなければ、黙って捨てられる最も危険なパターンを参照
3anthropic-beta の値を書き換え・追加する、モデルのマッピングや自動ダウングレードを行うベータ値の組み合わせと model は固定フィールドなので、ターン間で変わると衝突する400 thread_fingerprint_mismatch
4上流エラー時に別のチャネルや別のキーでリトライする1 つの応答 id から継続できるのは 1 回だけ。最初の試行が上流ですでに完了しているか、まだ実行中の可能性がある400 thread_already_continued または thread_continue_in_flight。キーが変わればパターン 1 も重なる
5上流のエラーを独自の形式に書き換えるerror.details.error_code が失われ、SDK が thread_not_found だと判断できず再送しない。存在しないモデルも 404 を返すので、ステータスコードだけでは区別できない404 がそのままアプリのコードに届く、または「モデルが存在しない」と誤解される
6OpenAI 互換エンドポイントだけを提供し、内部で Anthropic 形式に変換するChat Completions には thread を表せるフィールドがない使えない。thread を使えるのは Anthropic ネイティブの /v1/messages だけ
7上流が使用量を返さないとき、リクエストボディからローカルで使用量を推定するcontinue リクエストは新しいメッセージだけなので、サイズが元のごく一部になり、推定値が大きく下振れする請求額と実際の使用量が合わない。ゲートウェイは上流の usage で課金すべき

最も危険なパターン:thread フィールドが黙って捨てられる ​

ほとんどのパターンはエラーになります。パターン 2 だけは、たいていエラーになりません。

continue リクエストは、そもそも「新しいメッセージだけを送り、system も tools も送らない」設計です。ゲートウェイがボディをあらかじめ定義した構造体に読み込み、その構造体に thread フィールドがなかったとします。このフィールドは転送前に消え、上流には最後の 1、2 件のメッセージだけを含む普通のリクエストが届きます。

  • 新しいメッセージが tool_result で始まる場合(エージェントループではこれが普通です)、対応する tool_use が前にないので上流は 400 を返します。少なくとも気づくことはできます。
  • 普通の user メッセージで始まる場合、上流はそのまま回答します。ただし、システムプロンプトもツールも、それまでの文脈もありません。回答は「どこかおかしい」ものになりますが、エラーはどこにも出ません。

これは仮定の話ではありません。オープンソースのゲートウェイ new-api の v1.0.0-rc.46 では、anthropic-beta ヘッダーの値は結合されてそのまま転送されますが、ClaudeRequest 構造体には thread フィールドがありません。リクエストボディのパススルーを有効にしていないチャネルでは、ここで thread が捨てられます。型付きの構造体で Anthropic API を実装しているプロキシは、同じ問題がないか確認する価値があります。

クライアント側での確認方法: continue のターンで次の 2 つの兆候に注意してください。応答がそれまでの会話を明らかに「忘れている」場合と、usage の入力規模(input_tokens + cache_read_input_tokens + cache_creation_input_tokens)がそのターンで送った数件の新しいメッセージと同じくらいしかない場合です。どちらかに当てはまれば、thread が効いておらず、上流には断片しか届いていない可能性が高いと判断できます。thread 使用時の usage の数え方はドキュメントに書かれていないので、これは経験的な目安です。アラートには十分使えますが、請求の突き合わせには使わないでください。

ゲートウェイ側の対処 ​

ゲートウェイやプロキシを運用しているなら、最低限必要な変更は次のとおりです。

  1. thread をそのまま転送する。 構造体にフィールドを追加するか、/v1/messages のボディをパススルーします。上流が thread に対応していない場合は、フィールドを捨てるのではなく、thread_unsupported_request のような明確なコードを付けて 400 を返してください。そうすれば公式 SDK は、unsupported_request_behavior: continue_unthreaded を設定していれば、thread なしの通常のリクエストに自動で切り替えます。
  2. previous_message_id で振り分ける。 作成リクエストが成功したら「応答 id → 上流のチャネルとキー」を記録します。continue リクエストでは previous_message_id を引いて、同じキーに振り分けます。thread が保持される最長時間に合わせ、TTL は 1 時間で足ります。次のターンはそこから継続するので、新しい応答 id も毎回記録してください。
  3. continue リクエストを別のキーでリトライしない。 元のキーで失敗したらエラーをクライアントに返し、作成リクエストで再送してもらいます。別のキーで continue リクエストを送り直しても、失敗が 404 や thread_already_continued に変わるだけです。
  4. エラーは書き換えずに返す。 上流の HTTP ステータス、error.type、error.details(特に error_code と unsupported)を残してください。SDK の自動再送はこれらに依存しています。
  5. 固定フィールドを変えない。 ベータ値をリクエストごとに追加しないこと。モデルのマッピングは thread の間ずっと同じにし、thread 付きのリクエストでは別モデルへのダウングレードをしないでください。
  6. 上流の usage で課金する。 thread 付きのリクエストではローカル推定を止めるか、上流から usage が返らなかった場合は異常として記録します。
bash
# 作成:会話全体 + system + tools
curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: message-threads-2026-08-12" \
  -H "content-type: application/json" \
  -d '{"model":"claude-opus-5-5","max_tokens":1024,
       "cache_control":{"type":"ephemeral"},
       "system":"You are a code reviewer.",
       "messages":[{"role":"user","content":"Review this diff: ..."}],
       "thread":{"type":"create"}}'

# 継続:新しいメッセージだけ。system / tools は送らない。固定フィールド(model、トップレベルの cache_control、ベータヘッダー)は同じ値
curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: message-threads-2026-08-12" \
  -H "content-type: application/json" \
  -d '{"model":"claude-opus-5-5","max_tokens":1024,
       "cache_control":{"type":"ephemeral"},
       "messages":[{"role":"user","content":"Now check the error handling."}],
       "thread":{"type":"continue","previous_message_id":"'"$MESSAGE_ID"'"}}'

ゲートウェイを経由する場合は https://api.anthropic.com をゲートウェイのベース URL に置き換え、同じリクエストを送ります。2 つ目のリクエストを 1 つ目と同じキーに届けるのが、ゲートウェイの役割です。

クライアント側の対策 ​

  • 公式 SDK の thread ヘルパーを使う。 ヘルパーは次の処理を自動で行います。
    • ベータヘッダーを付ける。
    • continue リクエストから system と tools を省く。
    • thread_not_found、thread_already_continued、thread_continue_in_flight のときに会話全体で 1 回再送する。
    • フォールバックモデルの応答や拒否のあとは新しい thread を始める。
  • unsupported_request_behavior を continue_unthreaded にする。 ゲートウェイや組織の設定が thread を受け付けないとき、毎ターン再送するのではなく通常のリクエストに切り替わります。
  • 会話のコピーを自分で持つ。 ヘルパーは会話をメモリにしか持ちません。プロセスをまたぐセッションは自分で永続化してください。
  • ストリーミングでは message_stop を待ってから継続する。 id は message_start で届きますが、早く継続すると 404 になることがあります。ストリームが途中で切れたら、同じ continue リクエストをもう一度送ります。それが thread_already_continued か thread_continue_in_flight を返したら、会話全体で再送します。
  • pause_turn は空のメッセージで再開する。 応答が stop_reason: "pause_turn" で終わったら、"messages": [] で継続します。一時停止中に新しい user メッセージを送らないでください。

thread とプロンプトキャッシュ ​

continue リクエストには新しいメッセージしか入らないので、キャッシュはもう気にしなくてよいと考えがちです。ドキュメントの説明は逆で、thread はキャッシュしてくれません。

  • 最も簡単なのは、すべてのリクエストに同じトップレベルの cache_control を付ける方法です。API が各リクエストの最後のキャッシュ可能なブロックにブレークポイントを置くので、毎ターン、それまでの会話をキャッシュから読み、新しいターンをキャッシュに書き込みます。トップレベルの cache_control 自体も固定フィールドです。
  • thread 付きのリクエストに置けるブレークポイントは最大 4 つで、トップレベルの cache_control も 1 つに数えます。作成リクエストの system と tools に置いたブレークポイントは、continue リクエストのたびにもう一度数えられます。1 つでも超えると 400 thread_unsupported_request になります。
  • 一度送ったブレークポイントは移動も削除もできません。continue リクエストでは以前のメッセージを送り直せないからです。再送の前に古いメッセージのブレークポイントを外し、最後のブロックに 1 つだけ残してください。

キャッシュの損得は変わりません。Anthropic では、キャッシュの書き込みは入力料金の 1.25 倍(TTL 5 分)または 2 倍(TTL 1 時間)、読み取りは 0.1 倍です。ブレークポイントの位置が悪くヒット率が低ければ、キャッシュしない場合より高くつきます。仕組みと計算方法はプロンプトキャッシュの解説を参照してください。ゲートウェイにはもう 1 つ論点があります。キャッシュも上流のアカウント単位で保持されるので、パターン 1 のキーの固定がキャッシュヒット率も決めます。一度直せば両方の問題が解決します。

thread が使えない場面 ​

公式ドキュメント(2026-11-03 時点)によると、次の場面では使えません。

  • Message Batches:thread を含む項目は、結果で invalid_request_error になります。
  • トークンカウント:thread を含むリクエストは 400 になります。代わりに会話全体を system と tools 込みで数えてください。
  • コンパクション:オンデマンドのコンパクションは thread と併用できません。圧縮したい場合は自分で要約を書き、その要約から新しい thread を始めます。
  • Context editing:clear_thinking_20251015 と keep: "all" の組み合わせだけが使えます。
  • 組織の設定:HIPAA readiness や Inference hooks を有効にした組織では使えません。機能自体も ZDR の対象外です。
  • プラットフォーム:Claude API、Claude Platform on AWS、Google Cloud(Anthropic 管理)、Microsoft Foundry に対応しています。Amazon Bedrock は含まれていません。
  • 管理用 API がない:thread の一覧取得、読み取り、削除はできません。

出典 ​

「Claude Code がすでに thread を使っている」という話と、最初のトークンまでの時間の具体的な数字は、Anthropic の X アカウントの投稿だけが出典で、ドキュメントにはまだ記載がありません。この記事ではどちらも確定した事実として扱っていません。

よくある質問 ​

Claude Message Threads とは何ですか? ​

Anthropic が 2026-11-02 に Messages API でベータ提供を始めた機能です(ベータヘッダー message-threads-2026-08-12)。最初のターンで会話全体と thread: {type: "create"} を送り、以降は新しいメッセージと thread: {type: "continue", previous_message_id} だけを送ります。会話はサーバー側で組み立て直されます。thread は最後のリクエストから 5 分で期限切れになります。

ゲートウェイ経由だと continue リクエストが 404 thread_not_found になるのはなぜですか? ​

ドキュメントが挙げる原因は、thread の期限切れ、前のターンが保存されていない、前の応答をフォールバックモデルが書いた、前の応答がまだ生成中、そしてリクエストが別の organization・workspace・ユーザー・サービスアカウントから来た場合です。上流キーをローテーションするゲートウェイは最後の条件に当たります。previous_message_id をもとに、continue リクエストを同じキーへ振り分けてください。

thread を使ってもプロンプトキャッシュは必要ですか? ​

必要です。thread はキャッシュしてくれません。すべてのリクエストに同じトップレベルの cache_control を付けるのが最も簡単です。thread 付きのリクエストに置けるブレークポイントは最大 4 つです。

ゲートウェイが thread フィールドを落とすとどうなりますか? ​

上流には、それまでの文脈もシステムプロンプトもツールもない普通のリクエストが届きます。tool_result で始まっていれば 400 になります。user メッセージで始まっていれば、モデルは文脈なしで回答し、エラーはどこにも出ません。

Message Threads が使えないのはどんな場合ですか? ​

  • Message Batches、トークンカウント、オンデマンドのコンパクション、ほとんどの context editing。
  • HIPAA readiness や Inference hooks を有効にした組織。
  • ZDR が必要な場合。
  • Amazon Bedrock。

お困りですか?サポートへご連絡、またはグループにご参加ください。