Claude Code の「The model's tool call could not be parsed」エラーを直す
Claude Code Anthropic debugging mitmproxyClaude Code でこのエラーを繰り返し目にするなら——特に モデルが長く「考えた」あとに 出るなら:
● The model's tool call could not be parsed (retry also failed).
✳ Churned for 5m 47s
この記事では、すぐに適用できる対処法と、実際の原因の分析をまとめます。
先に重要な結論を述べておきます。これは本質的にモデル側の問題であり、あなたのローカル設定やネットワークの問題ではありません——根本原因は、大きなコンテキストでモデルが深く思考するうちに出力トークンの予算を使い切ってしまい、tool-use ブロックが送出されなくなることです(詳細は後述)。そして 一部の地域のユーザーでのみ再現します。同じバージョン・同じ設定でも、地域や経路によって発生率が大きく異なり、特に JP(日本)リージョンでは深刻なようです。周りの誰かが遭遇していなくても、あなたの環境が壊れているわけではありません。したがって本記事の対処法は 「緩和策」であって「根治」ではありません——発生率は大きく下げられますが、根本原因がモデル側にあるため完全になくなる保証はありません。完全に回避したいなら、一時的に旧バージョンのモデルに切り替えるのが確実です(例:Opus 4.8 から前世代の Opus / Sonnet へ)。旧モデルはこの問題の影響を受けないので、今のところ最も信頼できる回避策です。公式の修正が出たら新しいモデルに戻せば構いません。
中文版:解决 Claude Code 报错 The model’s tool call could not be parsed English: Fixing “The model’s tool call could not be parsed” in Claude Code
まず対処法から
このエラーは通常 ネットワークの問題ではありません。モデル側の挙動——extended thinking が出力トークンの予算を使い切ることが原因です。以下はいずれも発生率を大きく下げる 緩和策 です。完全に回避したい場合は方法 3(旧モデルへの切り替え)へ。
方法 1:always thinking を切る(推奨)
~/.claude/settings.json を編集します:
{
"alwaysThinkingEnabled": false
}
または Claude Code のセッション内で /config を使い、Always thinking をオフにします。
反映には新しいセッションを開始する必要があります——
settings.jsonはセッション起動時に一度だけ読み込まれるため、実行中のセッションで途中編集しても効きません。
オフにしてもモデルが劣化するわけではありません。必要と判断したときには引き続き思考しますが、毎ターン 強制的にトークン予算を思考に費やすことがなくなるだけです。オフにしたあと、同じ長時間タスクを effortLevel: high、さらには xhigh で走らせましたが、このエラーは二度と再現しませんでした。
方法 2:effort を下げる、またはコンテキストを新規に
effortLevelをhighから低いティア(例:low)に下げて、思考の予算上限を抑える- あるいは現在の会話コンテキストが既に大きい場合は、クリーンなセッションを開始 して入力トークンを減らし、出力に余地を残す
方法 3:一時的に旧バージョンのモデルへ切り替える(最も確実な回避策)
最初の二つは発生率を下げるだけで、根本原因がモデル側にあるため完全になくなる保証はありません。締め切りに追われていてこのエラーに邪魔されたくないなら、最も確実なのは 一時的に前世代のモデルへ戻すこと です——例:Opus 4.8 から前の Opus または Sonnet へ。旧モデルはこの問題の影響を受けません。公式の修正が出たら新しいモデルに戻しましょう。
対処だけが目的ならここで終わって構いません。以下はこれらの結論に至った過程です。
症状と最初の疑い
Claude Code(Opus 4.8 1M context、alwaysThinkingEnabled: true + effortLevel: high)で長時間タスクを走らせていて、このエラーに繰り返しぶつかりました。パターンは明確でした:
- 思考が長いほど発生しやすい
- 自動 リトライも失敗 ——同じコンテキストで再試行しても同じエラーになる
- タスクが完全にブロックされて先に進めない
最初に疑ったのはネットワークでした。SSE streaming は長時間の接続を使うため、途中でリセットされると tool-call の JSON が途中までしか届かず、壊れた JSON になって解析に失敗する——という仮説です。ただしこれは推測にすぎず、データで検証する必要がありました。
切り分け方:生の SSE ストリームを捕まえる
「ネットワークの切断」と「モデル側の問題」を区別する確実な手段は、Claude Code が実際に受け取った 生の API レスポンスのバイト列 を見ることです。Anthropic の /v1/messages は SSE のストリーミングレスポンスで、鍵になるのは そのストリームがどう終わるか です。
私は mitmproxy を Claude Code と出口の間にパッシブなタップとして挟み、リクエスト/レスポンスのヘッダーを一切書き換えず、ストリーミングを保ったまま各レスポンスを記録しました(診断対象そのものを乱さないように):
Claude Code ──HTTPS_PROXY=8080──▶ mitmproxy(8080) ──▶ 上流出口 ──▶ api.anthropic.com
│
└─ 生バイトをパッシブに tee → logs/*.sse
Claude Code を mitmproxy 経由にする要点は二つ:
~/.claude/settings.jsonのenvブロックでhttps_proxyを mitmproxy(http://127.0.0.1:8080)に向ける- mitmproxy の CA 証明書を
NODE_EXTRA_CA_CERTSに追加する(Claude Code は Node アプリで、これにより mitmproxy の TLS 中間者を信頼する)
{
"env": {
"https_proxy": "http://127.0.0.1:8080",
"HTTPS_PROXY": "http://127.0.0.1:8080",
"NODE_EXTRA_CA_CERTS": "/Users/you/.mitmproxy/mitmproxy-ca-cert.pem"
}
}
注意:
envブロックは セッション起動時 に読み込まれます。途中でファイルを編集しても、実行中のセッションには反映されません。
mitmproxy がレスポンスをストリーミングで通すとき、渡されるのは content-encoding で圧縮された生バイト(ここでは gzip)です。読む前に zlib.decompressobj(31) で展開してください。
決定的な証拠:usage フィールド
エラーを再現させたあと、該当する /v1/messages レスポンスを展開しました。イベント列はこうなっていました:
message_start model=claude-opus-4-8
content_block_start index=0 type=thinking ← thinking ブロックのみ
content_block_delta type=thinking_delta × 31 ← ずっと思考している
content_block_delta type=signature_delta ← thinking ブロックの署名
content_block_stop
message_delta stop_reason=tool_use ← モデルはツールを呼びたいと示す
message_stop ← そして終わってしまう
注目:stop_reason は tool_use なのに、ストリームのどこにも content_block_start type=tool_use イベントがありません。モデルは「ツールを呼びたい」という意図を示したのに、tool-use ブロックの本体は一度も送出されていません。
なぜか。答えは message_delta の usage フィールドにあります:
{
"type": "message_delta",
"delta": { "stop_reason": "tool_use" },
"usage": {
"output_tokens": 3165,
"output_tokens_details": { "thinking_tokens": 3120 },
"input_tokens": 37,
"cache_creation_input_tokens": 49060,
"cache_read_input_tokens": 0
}
}
計算してみると:
| 項目 | 値 |
|---|---|
output_tokens(このレスポンスの総出力) | 3165 |
うち thinking_tokens(思考に費やした分) | 3120 |
| 実際の内容(ツール呼び出しを含む)に残る分 | 45 |
最小限の tool-use ブロックでも、content_block_start のエンベロープ(id / name / 空の input)だけで約 30〜40 トークン、さらに input の JSON が必要です。45 トークンでは完全なツール呼び出しを収めきれません。
そのため API は stop_reason=tool_use(モデルの 判断)を送出したものの、対応する tool_use コンテンツブロックを書き出すトークンが残っていませんでした。Claude Code は「ツールを呼ぶと言っているのにツール呼び出しが含まれていない」レスポンスを受け取り、tool call could not be parsed と報告したわけです。
これでネットワークの疑いも晴れます。このストリームの HTTP ボディはきれいに終わっており(message_stop を完全に受信、接続リセットなし)、捕まえた他の数十件の呼び出しもすべて正常でした。問題はトークン予算の配分にあります。
なぜ「思考が長いほど悪化する」のか
因果の連鎖はこうです:
effortLevelが思考予算の 上限 を決めるalwaysThinkingEnabled: trueが 毎ターン 思考を強制する- 大きなコンテキスト(ここでは入力 + キャッシュで約 49K トークン)では、モデルは深く考え込んで止まりにくくなる
- 思考が出力トークン予算の大半を食い、tool-use ブロックに残る分が足りず、送出できない
- リトライも失敗 する。まったく同じコンテキストが毎回同じ枯渇を再現するからで、これは 決定論的な 失敗の特徴であり、ランダムなネットワークの不安定さではない証拠でもある
まとめ
tool call could not be parsedは通常ネットワークの問題ではなく、モデル側の挙動です。公式のapi.anthropic.comでは、出力トークンが思考に使い尽くされることが原因です。- これは モデル側の問題 で、一部の地域でのみ再現 します(JP リージョンが特に深刻なようです)——あなたのローカル設定やネットワークが壊れているわけではありません。
- 「リトライも失敗」は重要な手がかり——決定論的な失敗はコンテキスト/モデル側を指し、ランダムな失敗のほうがネットワークの可能性が高い。
- 生の SSE を捕まえるのが切り分けの有効な手段:mitmproxy のパッシブ記録 + 展開 +
usageを読む、で問題がどの層にあるかすぐ分かります。 - 大きなコンテキスト + always thinking は問題が起きやすい組み合わせです。コンテキストが大きいほどモデルは深く考え込み、出力予算を使い切りやすくなります。always thinking を切る・effort を下げるは緩和策。一時的に旧モデルへ戻す のが最も確実な回避で、その後は公式修正を待ちましょう。
この記事でデバッグの時間を節約できたなら、同じエラーに遭遇している人に共有してもらえると嬉しいです。