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 フィールドがなければ、黙って捨てられる | 最も危険なパターンを参照 |
| 3 | anthropic-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 がそのままアプリのコードに届く、または「モデルが存在しない」と誤解される |
| 6 | OpenAI 互換エンドポイントだけを提供し、内部で 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 の数え方はドキュメントに書かれていないので、これは経験的な目安です。アラートには十分使えますが、請求の突き合わせには使わないでください。
ゲートウェイ側の対処
ゲートウェイやプロキシを運用しているなら、最低限必要な変更は次のとおりです。
threadをそのまま転送する。 構造体にフィールドを追加するか、/v1/messagesのボディをパススルーします。上流が thread に対応していない場合は、フィールドを捨てるのではなく、thread_unsupported_requestのような明確なコードを付けて 400 を返してください。そうすれば公式 SDK は、unsupported_request_behavior: continue_unthreadedを設定していれば、threadなしの通常のリクエストに自動で切り替えます。previous_message_idで振り分ける。 作成リクエストが成功したら「応答id→ 上流のチャネルとキー」を記録します。continue リクエストではprevious_message_idを引いて、同じキーに振り分けます。thread が保持される最長時間に合わせ、TTL は 1 時間で足ります。次のターンはそこから継続するので、新しい応答idも毎回記録してください。- continue リクエストを別のキーでリトライしない。 元のキーで失敗したらエラーをクライアントに返し、作成リクエストで再送してもらいます。別のキーで continue リクエストを送り直しても、失敗が 404 や
thread_already_continuedに変わるだけです。 - エラーは書き換えずに返す。 上流の HTTP ステータス、
error.type、error.details(特にerror_codeとunsupported)を残してください。SDK の自動再送はこれらに依存しています。 - 固定フィールドを変えない。 ベータ値をリクエストごとに追加しないこと。モデルのマッピングは thread の間ずっと同じにし、
thread付きのリクエストでは別モデルへのダウングレードをしないでください。 - 上流の
usageで課金する。thread付きのリクエストではローカル推定を止めるか、上流からusageが返らなかった場合は異常として記録します。
# 作成:会話全体 + 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 つでも超えると 400thread_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 の一覧取得、読み取り、削除はできません。
出典
- Anthropic API リリースノート(2026-11-02、Message threads):https://platform.claude.com/docs/en/release-notes/api
- Message threads ドキュメント:https://platform.claude.com/docs/en/build-with-claude/message-threads
- Prompt caching ドキュメント:https://platform.claude.com/docs/en/build-with-claude/prompt-caching
- new-api のソースコード(v1.0.0-rc.46 の
ClaudeRequest構造体と Claude チャネルのアダプター):https://github.com/QuantumNous/new-api
「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。
