tool_resultとtool_useの400エラー — 正しい整形ルール
tool_useの直後にtool_resultを置き、tool_resultを先頭にまとめてtextはあとに置く整形ルールと、破ったときに出る400エラーの実際の文言をまとめます。
tool_resultの整形ルール
Claude APIはツール呼び出しの結果を、他のAPIのような専用のtoolロールではなくuserメッセージの中に埋め込みます。この構造には守るべき整形ルールが3つあり、破ると400エラーになります。
tool_resultブロックは、対応するtool_useブロックのメッセージの直後に置く。assistantのtool_useメッセージとuserのtool_resultメッセージの間に、他のメッセージを挟めませんtool_resultを含むuserメッセージでは、tool_resultブロックをcontent配列の先頭に置く。テキストを書くなら、すべてのtool_resultよりあと- 同じターンで解決していないサーバーツール(API側が実行するツール)の呼び出しが残っている場合、返すuserメッセージはtool_resultブロックだけで構成する。テキストを1つでも混ぜると、ターンが早期に終わったと解釈されます
tool_resultより先にテキストを置くと400になる
もっとも基本的な違反は、tool_resultより先にテキストを置くことです。
{
"role": "user",
"content": [
{ "type": "text", "text": "Here are the results:" },
{ "type": "tool_result", "tool_use_id": "toolu_01" }
]
}このリクエストは400で失敗し、次の文言が返ります。
tool_use ids were found without tool_result blocks immediately after: toolu_01PjgRJLbXrXEMZwDNYLnBqk. Each tool_use block must have a corresponding tool_result block in the next message.直すのは単純です。tool_resultを配列の先頭に移動し、テキストをあとに回すだけで通ります。
{
"role": "user",
"content": [
{ "type": "tool_result", "tool_use_id": "toolu_01" },
{ "type": "text", "text": "What should I do next?" }
]
}クライアント側のツール(自分のコードで実行するツール)だけを呼んだ応答であれば、この並び順を守れば問題ありません。次の質問のような新しいテキストをtool_resultのあとに続けるのは正当な使い方です。
この制約が生まれる理由は、Claude APIが他社のAPIのように専用のtoolロールを持たないことにあります。ツール呼び出しの結果はuserメッセージの一部として送り返され、roleだけでは「これはユーザーの発言か、ツールの結果か」を区別できません。順序と位置というメッセージ構造そのものが、その区別を担っています。だからこそ、並び順を1つ間違えるだけで、APIはメッセージの意味を正しく解釈できなくなります。
中身が空、または画像・文書を含むtool_result
tool_resultのcontentフィールドは任意です。ツールが実行はできたが返す値が特に無い場合、contentを省略した最小構成でも有効です。
{ "type": "tool_result", "tool_use_id": "toolu_01A09q90qw90lq917835lq9" }contentには文字列のほか、text・image・document・search_result型のブロックを配列で渡せます。天気の数値と一緒にグラフ画像を返す、検索結果のドキュメントを添える、といった構成も同じtool_resultブロック1つの中で表現します。ステータス更新のように返す値がそもそも無いツール(「既読にする」だけの操作など)は、無理に"done"のような文字列を作らず、contentを省略した最小形で構いません。
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": [
{ "type": "text", "text": "1週間の気温推移です" },
{ "type": "image", "source": { "type": "base64", "media_type": "image/png", "data": "..." } }
]
}このようにtextブロックとimageブロックを同じcontent配列に並べれば、説明文とグラフ画像を1つのtool_resultにまとめて返せます。ブロックの順番自体は表示や解釈に影響しないため、テキストを先に置いても後に置いても構いません。
複数のtool_useをまとめて返す
Claudeは1つのassistantターンで複数のtool_useブロックを返すことがあります。並列に実行するか順番に実行するかは呼び出し側が決めてよく、APIは実行順序を指定しません。ただし返し方には決まりがあります。すべてのtool_resultを、1つのuserメッセージにまとめることです。
// ❌ 誤り: tool_resultごとに別のuserメッセージ
[
{ "role": "assistant", "content": [/* tool_use_1, tool_use_2 */] },
{ "role": "user", "content": [/* tool_result_1 */] },
{ "role": "user", "content": [/* tool_result_2 */] }
]
// ✅ 正しい: 1つのuserメッセージに全部まとめる
[
{ "role": "assistant", "content": [/* tool_use_1, tool_use_2 */] },
{ "role": "user", "content": [/* tool_result_1, tool_result_2 */] }
]tool_resultと対応するtool_useの突き合わせはtool_use_idで行われるため、配列内の並び順自体は結果の対応関係に影響しません。とはいえ、まとめて1つのuserメッセージに入れておけば、この突き合わせが常に安定して機能します。ツール結果の整形ミスは、並列ツール呼び出しがうまく動かないときの最多の原因です。なかでもtool_resultを個別のuserメッセージに分けて送る書き方は、Claudeが以降の並列呼び出しを避けるようになる引き金になります。
実行を一部スキップした場合(順番に実行していて途中の呼び出しが失敗した、など)も、そのtool_use_idに対応するtool_resultは省略できません。is_error: trueを付けて返す書き方はtool_resultのis_errorとエラーメッセージの書き方にまとめています。
サーバーツールが未解決のまま残る場合の400エラー
Web検索やコード実行のようなサーバーツールは、API側が内部で実行するためtool_resultを自分で用意する必要がありません。ただし、同じターンでクライアント側のツール(自分のコードで実行するツール)も同時に呼ばれた場合、サーバーツールの結果ブロックはまだ届かず、応答はstop_reason: "tool_use"のまま終わります。
この状態で返信するuserメッセージには、クライアント側のtool_resultブロックだけを含めます。テキストを1つでも混ぜると、APIはそのメッセージでターンが終わったと解釈します。しかしサーバーツールの呼び出しはまだ解決していないため、直接呼ばれたサーバーツールの場合はこの状態のまま400invalid_request_errorになります。
web_fetch tool use with id srvtoolu_01HxbWnMRmbWyMfUtJKC45rA was found without a corresponding web_fetch_tool_result blocktools配列から、待機中だったサーバーツールの定義自体を外して再送した場合も、同様に400になります。文言の末尾は次のとおりです。
but no `web_fetch` tool was providedエラーの文言を見れば、どちらの違反かを切り分けられます。「tool_use ids were found without tool_result blocks」はクライアントツールの並び順の問題、「... was found without a corresponding ..._tool_result block」はサーバーツールが解決しないままターンを終えた問題です。tools配列は、待機中のサーバーツールを含めたまま変えずに再送するのが基本です。
このstop_reason: "tool_use"のまま応答が返ってきた場合、クライアント側のtool_use_idに対応するtool_resultだけを含んだuserメッセージを組み立てて送り返します。サーバーツールの結果はAPI側が保持しているため、こちらでweb_fetch_tool_resultのようなブロックを自作して足す必要はありません。tools配列を変更せずそのまま再送すれば、サーバーツールの実行が続きから再開されます。
エラーにはならないが応答が壊れるケース
tool_resultのあとにテキストを置くこと自体は、前述のとおり構造として正当です。ただし、そのテキストの中身によっては別の問題が起きます。tool_resultのすぐあとに「結果はこちらです」のような要約めいたテキストを差し込むと、Claudeが空の応答を返すようになることがあります。
// 避けたい書き方: tool_resultの直後に要約テキストを添える
{
"role": "user",
"content": [
{ "type": "tool_result", "tool_use_id": "toolu_123", "content": "6912" },
{ "type": "text", "text": "Here's the result" }
]
}
// 推奨: tool_resultだけを返す
{
"role": "user",
"content": [
{ "type": "tool_result", "tool_use_id": "toolu_123", "content": "6912" }
]
}これは400エラーにはなりません。それでもClaudeは、tool_resultのあとに毎回ユーザー側のテキストが挿入されるパターンを学習し、以降正確に2〜3トークンだけの空の応答をstop_reason: "end_turn"とともに返すようになることがあります。ツール結果を返すだけのターンでは、tool_resultブロックのみを送り、Claude自身に応答を作らせるのが安全です。ユーザーからの新しい質問(「次はどうすればいい?」)を続けるのは、これとは別の正当な使い方です。
tool_resultの中身は外部由来として扱う
整形ルールとは別に、tool_resultの中身そのものの扱いにも注意が要ります。ツール結果には、Webページ・受信メール・ユーザーのアップロードファイル・外部APIの応答など、自分の管理下にないコンテンツが入り込みます。この中身は、自分の管理下にない外部データとして扱うのが前提になります。悪意ある第三者がその内容に介入できる立場にあれば、Claudeを別方向へ誘導しようとする指示文を埋め込む余地があります(間接的なプロンプトインジェクション)。
信頼できない可能性のあるコンテンツは、systemプロンプトや素のtextブロックではなくtool_resultブロックの中に留めておきます。整形ルールを守ることは、単に400エラーを避けるだけでなく、どのコンテンツが「外部由来」かをAPI側にも正しく伝えることにつながります。外部データをsystemプロンプト側に混ぜて渡す実装は、指示と外部データの境界を自らあいまいにする設計になりがちです。
Agent SDKに任せる選択肢
これらの整形ルールを自前で管理したくない場合、SDKのTool Runnerがtool_useの検出からtool_resultの整形、複数呼び出しのバッチ化までを内部で処理します。並び順やcontent配列の位置といった細部を毎回自分のコードでチェックしなくて済む分、実装ミスによる400エラーの発生源そのものを減らせます。
手動でツール呼び出しループを組んでいて、実行順序や独自のバッチ処理を細かく制御したい場合に限り、本記事の整形ルールを直接扱う必要があります。ツール呼び出しループの自動化についてはTool RunnerでAnthropic APIのツール呼び出しループを自動化するにまとめています。
ツール定義そのもの(スキーマの書き方・モデル選択・料金体系)を含めたAnthropic API全体の入口はAnthropic API完全ガイドにまとめています。
動作するリクエストの例
以下は、1つのクライアントツールを呼び、その結果を正しい形式で返す一連のやり取りです。
curl -sS https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-opus-5",
"max_tokens": 1024,
"tools": [{
"name": "get_weather",
"description": "Get the current weather in a given location",
"input_schema": {
"type": "object",
"properties": {"location": {"type": "string"}},
"required": ["location"]
}
}],
"messages": [
{"role": "user", "content": "What is the weather in Tokyo?"},
{"role": "assistant", "content": [
{"type": "tool_use", "id": "toolu_01", "name": "get_weather",
"input": {"location": "Tokyo, Japan"}}
]},
{"role": "user", "content": [
{"type": "tool_result", "tool_use_id": "toolu_01", "content": "22 degrees, sunny"}
]}
]
}'3つ目のメッセージが守っているのは、tool_resultだけを含み、直前のassistantメッセージの直後に置かれているという点です。ここに1文字でもテキストが混ざると、前述のいずれかの400エラーか、空応答の原因になります。
まとめ
tool_resultの整形は3つのルールに集約できます。tool_useの直後に置く、tool_resultをcontent配列の先頭に置きテキストはあとに置く、未解決のserver toolがあるときはtool_resultだけを返す。この3つを守れば、400エラーの大半は避けられます。
複数のツール呼び出しをまとめて返す場面と、tool_resultの直後に不要なテキストを挟む場面は、エラーにはならなくても挙動が壊れる別種の落とし穴です。エラーメッセージの文言(tool_use ids were found without...か... tool_result blockか)を手がかりに、まず400の種類を切り分けてから対処すると、原因の特定が早くなります。ここで扱っているのはメッセージ構造そのものの整形ルールで、400という同じステータスコードでもリクエスト全体の妥当性(パラメーターの型・料金上限など)に起因するエラーは別範囲です。そちらの分類とリトライ設計はClaude APIのエラーハンドリング設計にまとめています。