Claudeを使って開発していると、突然「tool result could not be submitted」というエラーに遭遇することがあります。しかも、このエラーはドキュメントに詳しく書かれていないことが多く、原因の特定に時間がかかってしまいます。
私自身も Claude の tool use 機能を実装しているときに何度かこのエラーで詰まりました。試行錯誤の末にパターンを掴めてきたので、今回は原因と対処法をまとめておきます。
このエラーはどんな場面で出るか
「tool result could not be submitted」は、Claude の tool use(ツール使用)機能を利用しているときに発生するエラーです。具体的には次のような流れで起きます。
- ユーザーがメッセージを送信する
- Claude がツール呼び出しを判断して
tool_useブロックを含む応答を返す - あなたのコードがそのツールを実行して結果を取得する
- その結果を
tool_resultとして Claude に送り返す
この4番目のステップが失敗すると、このエラーが出ます。エラーメッセージだけ見ると「Claudeが結果を受け取れなかった」ように読めますが、実際には原因がいくつかあります。
主な原因と対処法
1. ツール結果が大きすぎる(最も多い原因)
これが一番よく見るケースです。たとえばウェブ検索ツールで取得したHTMLをそのまま返したり、データベースから大量のレコードを返したりすると、コンテキストウィンドウを超えてしまいます。
Claude のコンテキスト制限(現在の多くのモデルで20万トークン前後)を超えると、ツール結果が受け付けられなくなります。
対処法: ツール結果を必ず前処理して、本当に必要な情報だけを返すようにしましょう。
# ❌ 生のHTMLをそのまま返す(危険)
def search_web(query: str) -> str:
response = requests.get(f"https://example.com/search?q={query}")
return response.text # 何万文字もある可能性がある
# ✅ 必要な情報だけ抽出して返す(正しい)
def search_web(query: str) -> str:
response = requests.get(f"https://example.com/search?q={query}")
soup = BeautifulSoup(response.text, 'html.parser')
# タイトルとスニペットだけ抽出
results = []
for item in soup.select('.result')[:5]: # 上位5件だけ
title = item.select_one('h3')
snippet = item.select_one('.snippet')
if title and snippet:
results.append(f"タイトル: {title.text}\n概要: {snippet.text[:200]}")
return "\n\n".join(results) if results else "結果が見つかりませんでした"目安として、一つのツール結果は 5,000〜10,000トークン以内(日本語で約10,000〜20,000文字以内)に収めるようにすることをおすすめします。
2. tool_use_id が一致していない
これも意外と多いミスです。Claude が返した tool_use ブロックには id フィールドがあり、その ID を tool_result の tool_use_id に正確に指定しなければなりません。
# Claudeの応答から tool_use ブロックを取得した場合
response = anthropic.messages.create(...)
# ❌ IDをハードコードしたり、省略する
tool_results = [{
"type": "tool_result",
"tool_use_id": "toolu_01", # ハードコードは危険
"content": result_content
}]
# ✅ Claudeが返したIDをそのまま使う
for block in response.content:
if block.type == "tool_use":
tool_result = {
"type": "tool_result",
"tool_use_id": block.id, # ← Claudeが返したIDをそのまま使う
"content": str(tool_output)
}特に複数のツールが同時に呼ばれる場合(parallel tool use)、それぞれの id を正しく対応させる必要があります。
3. content フィールドの型が間違っている
tool_result の content フィールドには文字列か、画像などを含む配列を指定します。None や数値をそのまま渡すと受け付けられないことがあります。
# ❌ None や数値をそのまま渡す
"content": None
"content": 42
# ✅ 必ず文字列に変換する
"content": str(result) if result is not None else "ツールの実行結果がありませんでした"エラーが発生した場合も空文字ではなく、エラーを示す文字列と is_error: true を使いましょう。
# ツールがエラーを返した場合の正しい書き方
{
"type": "tool_result",
"tool_use_id": block.id,
"content": "エラー: データベースへの接続がタイムアウトしました",
"is_error": True # これでClaudeはエラーを認識してリカバリを試みる
}4. ネットワーク・タイムアウト問題
ツールの実行に時間がかかりすぎると、API接続がタイムアウトすることがあります。特に外部APIを呼び出すツールや、大きなファイルを処理するツールは注意が必要です。
import anthropic
import httpx
# タイムアウトを明示的に設定する
client = anthropic.Anthropic(
http_client=httpx.Client(timeout=60.0) # 60秒に延長
)ツール自体の実行時間が長い場合は、処理を分割するか、非同期処理を検討してください。
5. メッセージ履歴の形式が崩れている
会話を続けていくうちに、messages 配列の構造が正しくなくなることがあります。tool use を使う場合、メッセージの順番と形式に厳密なルールがあります。
# 正しいメッセージ履歴の構造
messages = [
# 1. ユーザーのメッセージ
{"role": "user", "content": "東京の天気を教えて"},
# 2. Claudeのツール呼び出し応答
{"role": "assistant", "content": [
{"type": "text", "text": "天気を調べます。"},
{"type": "tool_use", "id": "toolu_01", "name": "get_weather", "input": {"city": "Tokyo"}}
]},
# 3. ツール結果(user ロールで送る)
{"role": "user", "content": [
{"type": "tool_result", "tool_use_id": "toolu_01", "content": "東京: 晴れ、25°C"}
]},
# 4. 次のメッセージ...
]assistant のメッセージの後に必ず user のツール結果を挟まないと、このエラーが出やすくなります。
デバッグ手順のまとめ
このエラーで詰まったときは、次の順番で確認してみてください。
まず tool_use_id の一致を確認します。Claude が返した ID と送信している ID が完全に一致しているかを print で確認しましょう。次に結果のサイズを確認します。len(result_content) を出力して、文字数が異常に多くないかチェックします。そして content の型を確認します。type(result_content) が str であることを確認しましょう。最後にメッセージ配列の構造を確認します。role の順番と content の形式が正しいかを見直します。
よくある疑問
Q. このエラーは Claude の側のバグですか?
多くの場合、実装側の問題です。ただし、まれに Claude 側の一時的な問題でも出ることがあります。同じコードで再試行して成功するなら、一時的な問題の可能性が高いです。
Q. stream モードでも同じエラーが出ます。対処法は変わりますか?
基本的な原因は同じです。ただし stream モードでは MessageStreamEvent のハンドリングが複雑になるので、まず非 stream モードで動作確認してから stream に切り替えることをおすすめします。
最後に
「tool result could not be submitted」は、メッセージのひとこと見ただけでは何が悪いのか分かりにくいエラーです。ただ、上記のパターンのいずれかに当てはまることがほとんどなので、順番に確認していけば解決できるはずです。
特に「ツール結果が大きすぎる」は見落としがちです。開発初期は小さなデータで動いていても、本番データになったとたんにエラーが出るケースをよく見るので、早めに結果サイズの制限を実装しておくことをおすすめします。