tool_resultのis_errorでエラーを正しく伝える書き方
is_errorに書くメッセージの質でClaudeの立て直し方が変わります。良い例・悪い例、必須パラメーター欠落時のリトライ挙動、サーバーツールとの違いをまとめます。
is_errorの書き方でClaudeの立て直し方が変わる
ツールの実行が失敗したとき、tool_resultブロックのcontentにエラー内容を書き、is_error: trueを添えて返します。Claudeはこの内容を読んで、ユーザーへの説明を組み立てたり、別のやり方を試したりします。contentに何を書くかで、Claudeがそのエラーから立て直せるかどうかが変わります。
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "ConnectionError: the weather service API is not available (HTTP 500)",
"is_error": true
}is_errorが対象にするのは、クライアントツール(自分のコードで実行するツール)です。Web検索のようなサーバーツール(API側が実行するツール)のエラーはAPI側が内部で処理するため、is_errorを自分で扱う必要はありません(後述)。対象になる失敗は大きく2種類あります。ツールは正しく呼ばれたが実行結果がエラーだった場合と、そもそも呼び出し自体が無効だった場合です。
「failed」で終わらせない — 指示的なメッセージを書く
"failed"のような一般的なエラー文言だけを返すと、Claudeは何が起きたのか、次に何を試せばよいのかを推測するしかありません。何が問題で、Claudeが次に何を試すべきかを含めた指示的なメッセージにすると、推測させずに回復や調整の材料を渡せます。
| 悪い例 | 良い例 |
|---|---|
"failed" | 良い例"Rate limit exceeded. Retry after 60 seconds." |
"error" | 良い例"Missing required 'location' parameter" |
"invalid" | 良い例"'unit' must be 'celsius' or 'fahrenheit', got 'C'" |
"unauthorized" | 良い例"API key expired. Re-authenticate before retrying." |
"not found" | 良い例"No customer with id 'cus_98x2'. Check the id and retry." |
"timeout" | 良い例"Upstream service timed out after 30s. Safe to retry once." |
contentは必ずしも1文の文字列である必要はありません。他のtool_resultと同じく、textブロックを複数並べたり、関連するdocumentブロックを添えたりできます。エラーの原因が複雑で、状況の説明とログの抜粋を分けて渡したいような場面では、この構造化された形が役立ちます。
良い例はどれも2つの要素を含みます。何が起きたか(レート制限、パラメーター欠落、値の不正)と、次にどうすればよいか(60秒待つ、locationを補う、値を修正する)です。この2要素があるかどうかで、Claudeが自力で訂正できるエラーと、ユーザーへの報告に回すしかないエラーの分かれ方が変わります。
必須パラメーター欠落はClaudeが自分で訂正する
Claudeのツール呼び出しが無効(必須パラメーターの欠落など)だった場合、通常はClaudeがそのツールを正しく使うための情報が足りていなかったサインです。開発中にこのエラーを見かけたら、まず疑うのはツール側のdescriptionです。パラメーターの意味・形式・単位をより詳しく書き直してから再試行すると、同じ入力でも通るようになることがあります。プロダクション環境で毎回発生するようであれば、ランタイムのtool_resultで訂正させる前に、まずdescriptionの見直しを検討する価値があります。
会話を先に進めたい場合は、エラーを示すtool_resultを返して継続することもできます。
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "Error: Missing required 'location' parameter",
"is_error": true
}ツール呼び出しが無効な場合、または不足パラメーターがある場合、Claudeは謝罪する前に2〜3回、訂正を試みます。つまり1回目のis_errorで終わりではなく、渡した情報をもとに入力を組み直して再度ツールを呼んできます。ここでも、contentに何が欠けているかを具体的に書いておくほど、この自己訂正が的確に働きます。
無効な呼び出しが起きる原因は1つではありません。is_errorで1件ずつ訂正させる前に、症状から原因を特定しておくと同じ失敗の再発を防げます。
| 症状 | よくある原因 | 対処 |
|---|---|---|
| 存在しないパラメーターを送ってくる | よくある原因strict未使用時のモデルの過剰生成 | 対処strict: trueを追加 |
| enumの範囲外の値を送ってくる | よくある原因strict未使用、またはenumが大きすぎる | 対処enumを絞る、またはinput_examplesで妥当な値を示す |
| 意図したツールを呼ばない | よくある原因ツール名の衝突、スキーマが汎用的すぎる | 対処ツール名の重複を確認し、input_examplesで用途を明確にする |
これらはいずれもdescriptionやinput_schema側の設計に手を入れる話で、is_errorのcontentをどう書くかという本記事の主題とは層が異なります。実行時の訂正(is_error)と設計時の予防(description・strict・input_examples)を、両輪として使い分けるのが実務的です。
実際のリクエストで確認する
get_weatherが接続エラーを起こした状況を、is_error付きのtool_resultで伝えるリクエストは次のようになります。
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": "ConnectionError: weather service unreachable. Safe to retry once after a short delay.",
"is_error": true}
]}
]
}'is_error: trueがあっても、メッセージ配列としての整形ルール(tool_resultを先頭に置く、直前のassistantメッセージの直後に置く)は変わりません。エラーかどうかはis_errorフィールドが担い、位置関係は通常のtool_resultとまったく同じです。整形自体を間違えると、is_errorの中身がどれだけ丁寧でも400エラーで弾かれ、Claudeに読んでもらう前に会話が止まります。
サーバーツールのエラーはis_errorの対象外
Web検索のようなサーバーツールでネットワーク障害などが起きた場合、Claudeが内部で透過的に処理し、代替の応答や説明をユーザーに提示しようとします。クライアントツールとは異なり、サーバーツールの結果には自分でis_errorを設定する必要がありません。
Web検索固有のエラーコードには次のようなものがあります。
| コード | 内容 |
|---|---|
too_many_requests | 内容レート制限超過 |
invalid_input | 内容検索クエリのパラメーターが不正 |
max_uses_exceeded | 内容Web検索ツールの最大使用回数を超過 |
query_too_long | 内容クエリが上限の長さを超過 |
unavailable | 内容内部エラーが発生 |
これらはAPIのレスポンス内に情報として現れるものであり、開発者がtool_resultを作ってis_error: trueを付け返す対象ではありません。クライアントツールのエラーハンドリングと同じ設計で扱おうとすると、存在しないコードパスを実装することになります。
クライアントツールとサーバーツールの違いは、実行の主体がどちらにあるかです。クライアントツールはあなたのコードが実行し、結果をAPIへ送り返す必要があります。サーバーツールはAPIが内部で実行まで完結させ、成功も失敗もレスポンスの中にすでに含めて返します。is_errorという仕組みは、実行結果を送り返す側(開発者)がその成否を伝えるためのフィールドなので、実行そのものを担っていないサーバーツールには最初から出番がありません。
複数呼び出しのうち一部だけ失敗したとき
1つのassistantターンで複数のtool_useが返ることがあります。順番に実行していて途中の呼び出しが失敗した、あるいは意図的に一部の実行をスキップした場合でも、そのtool_use_idに対応するtool_resultは省略できません。 is_error: trueと簡潔な説明を付けて、必ず返します。
{
"type": "tool_result",
"tool_use_id": "toolu_02",
"content": "Skipped: an earlier tool call in this batch failed",
"is_error": true
}対応するtool_resultが1つでも欠けると、次のリクエストは整形ルール違反として400エラーになります。「失敗したから何も返さない」という省略は選べません。
エラー内容の記述と、指示の埋め込みは別物
指示的なメッセージを書くという原則には、注意すべき境界があります。tool_resultの中身は、Claudeにとって信頼度の低い外部由来のデータとして扱われます。そのため、contentの中に開発者側の一般的な指示(「常に日本語で答えて」「今後は箇条書きでまとめて」など、そのツールの実行結果とは無関係な振る舞いの指示)を紛れ込ませると、Claudeがその指示への追従を拒んだり、ユーザーに確認を求めたりすることがあります。
この挙動と、is_errorで推奨される「次に何を試すべきか」を書くこととは矛盾しません。"Retry after 60 seconds."のような一言は、そのツール呼び出し自体の結果としての回復手がかりであり、会話全体の振る舞いを変える指示ではないからです。ツールの実行結果と無関係な振る舞いの指示は、tool_resultの外(tool_resultのあとに続けるuserターンなど)に置くのが安全な切り分け方です。
リトライで直らないエラーには「待つ・諦める」を書く
Claudeの自己訂正は、原因がリクエスト側の記述ミス(パラメーター名の誤り、値の形式違反など)であることを前提にした挙動だと考えられます。原因が呼び出し側にないエラー、たとえば外部サービスの障害やアカウントの権限不足では、Claudeが入力を書き直しても結果は変わりません。
こうしたケースでは、「次に何を試すべきか」の答えが待つか諦めるのどちらかになります。"Retry after 60 seconds."のように待機時間を明示する、あるいは"This account does not have permission for this operation. Do not retry."のように再試行の余地が無いことを明示すると、Claudeが無意味な訂正を繰り返す回数を減らせる可能性があります。指示的なメッセージという原則は、成功する訂正だけでなく、訂正しても無駄なケースにも同じように適用できます。
自前で組むか、SDKに任せるか
ここまでの内容は、ツール呼び出しループを自分のコードで回している場合に直接関わってきます。Tool RunnerでAnthropic APIのツール呼び出しループを自動化するような設計であれば、ツール実行の例外をis_error付きのtool_resultへ変換する処理はSDK側が担い、ここで書いたメッセージ品質の作り込みだけが呼び出し側の責任として残ります。ツール呼び出し以外のHTTPレベルのエラー(レート制限・認証・タイムアウトなど)まで含めた設計はClaude APIのエラーハンドリング設計で扱っています。両者は別のレイヤーのエラーで、is_errorが対象にするのはツールの実行結果、あちらが対象にするのはリクエスト自体の成否です。
まとめ
is_error: trueのcontentは、単なるエラーの通知ではなく、Claudeがその先どう動くかを左右する入力です。「何が起きたか」と「次に何を試すべきか」の2つを具体的に書くこと、パラメーター欠落は2〜3回の自己訂正が働く前提で書くこと、サーバーツールにはis_errorが要らないこと、複数呼び出しの一部失敗でもtool_result自体は省略しないこと。この4点を押さえておけば、雑な"failed"よりずっと実用的なエラーハンドリングになります。
もう1つ覚えておきたいのは、実行時の訂正(is_errorのメッセージ)と設計時の予防(description・strict・input_examples)は別のレイヤーだという点です。同じ種類の無効な呼び出しが繰り返し起きるなら、is_errorで毎回訂正させるより、ツール定義側を直すほうが根本的な対処になります。そして、そのcontentに書くのはあくまでツール自身の実行結果の説明にとどめ、会話全体の振る舞いに関わる指示を混ぜないこと。この境界を守ると、Claudeがtool_resultの内容を意図どおりに扱いやすくなります。