Claude Media
Claude Codeの--resumeでキャッシュが効かず、コストが20倍になった問題

Claude Codeの--resumeでキャッシュが効かず、コストが20倍になった問題

--print --resumeでキャッシュが再利用されずコストが20倍になった回帰の原因と、v2.1.90で入った修正、症状の確認方法をまとめます。

claude --print --resumeでセッションを再開すると、会話が長くなってもプロンプトキャッシュが一切再利用されない回帰が2026年3月に報告されました。原因は再開時にmessages[0]の中身が新規セッションと食い違い、キャッシュのプレフィックス一致が壊れることでした。公式の修正はv2.1.90で入っています。

--resumeで何が起きたか — cache_readが伸びずコストが20倍に

--print --resumeは、既存セッションを非対話(ヘッドレス)で再開し、1メッセージだけ送って終了するコマンドです。Discord botやCI連携など、外部システムからClaude Codeを1回のプロセス起動として呼び出す用途で使われます。

GitHub issue #34629の報告者は、Ubuntu上でDiscord bot(claude --print --model <model> --resume <session-id> --output-format stream-json --verboseをstdin経由のプロンプトで呼ぶ構成)を運用していました。v2.1.68では正常だったキャッシュ挙動が、v2.1.76では壊れていることを、同一セッションに3通以上のメッセージを送る検証で確かめています。起点がv2.1.69である点は、のちのコメントによるバンドル解析が根拠です。

正常時(v2.1.68)と回帰後(v2.1.76)で、同じセッションを送り続けたときの定常状態を比較すると次のとおりです。

バージョンcache_read(定常)cache_create(定常)1メッセージあたりの概算コスト
v2.1.68(正常)cache_read(定常)37,295cache_create(定常)8021メッセージあたりの概算コスト約$0.02
v2.1.76(回帰後)cache_read(定常)14,569cache_create(定常)55,9541メッセージあたりの概算コスト約$0.36

issue本文は「約20倍のコスト増」と表現しており、上の定常状態の数値だけでも0.36ドルは0.02ドルの18倍です。v2.1.76のcache_readはClaude Codeのシステムプロンプト分(約14.5kトークン)で頭打ちになり、会話ターンはメッセージのたびにcache_createとして丸ごと課金されていました。

報告者はモデルやコンテキスト長を変えたテストも行っており、退行がモデル非依存でバージョン依存だと確認しています。

バージョンモデルコンテキストcache_readは伸びるか定常コスト/メッセージ
v2.1.68モデルopusコンテキスト200kcache_readは伸びるか伸びる定常コスト/メッセージ約$0.02
v2.1.68モデルopus[1m]コンテキスト1Mcache_readは伸びるか伸びる定常コスト/メッセージ約$0.02
v2.1.76モデルopusコンテキスト200kcache_readは伸びるか14.5kで固定定常コスト/メッセージ$0.04〜$0.40(増加)
v2.1.76モデルopus[1m]コンテキスト1Mcache_readは伸びるか14.5kで固定定常コスト/メッセージ$0.35〜$0.40
v2.1.76モデルopus-4-5(20251101)コンテキスト200kcache_readは伸びるか14.5kで固定定常コスト/メッセージ$0.04〜$0.40(増加)

なぜキャッシュが壊れたか — messages[0]の非対称性

キャッシュはAPIリクエストのmessages配列の先頭が前回と一致する範囲で再利用されます。この回帰では、新規セッションと再開セッションとでmessages[0]の組み立て方そのものが異なっていました。

issueのコメントで、投稿者の一人がMITMプロキシで複数バージョンのAPIリクエストを実際にキャプチャして原因を特定しています。新規セッションでは、デフォルトツールやMCP・skillsの案内といったシステムリマインダーが最初のメッセージ(messages[0])にまとめて入り、合計で約13.4KBになります。一方、再開セッションではmessages[0]に現在日時などの最小限の情報(約352B)しか入らず、同じ案内は会話の末尾(messages[N])に追記される構造でした。

くらべる

messages[0]の組み立て方の違い

約13.4KB

新規セッション

messages[0]に案内4種とユーザー入力がまとまって入ります。課金用のバージョンハッシュはmessages[0]から算出され、cache_controlの境界もmessages[0]の末尾に付きます。

約352B

再開セッション

messages[0]には現在日時などの1種だけが入り、案内群は末尾のmessages[N]に追記されます。そのためハッシュが別の値になり、cache_controlの境界もmessages[N]の末尾へ動きます。

3つの差はどれもキャッシュのプレフィックス一致を壊しますが、根本原因はmessages[0]の内容そのものが違う点だと分析されています。この案内群を持つdeferred_tools_deltaという添付タイプがv2.1.69で新たに導入されており、v2.1.68のコードには存在しないことも確認されています。v2.1.68では新規・再開のどちらもmessages[0]が最小限の情報だけで一致していたため、この非対称性自体が発生していませんでした。

分析コメントでは、修正の方向性として複数の案が挙げられていました。再開時にも案内群をmessages[0]と同じ位置へ注入する案、案内群をメッセージではなくシステムプロンプト側のsystem[]パラメータへ移す案、課金用ハッシュの算出時にシステムリマインダーを除外する案などです。実際にv2.1.90で採用された修正がこれらのどれに近いかはchangelogから読み取れませんが、対象範囲(--resume・deferred tools・MCPサーバー・カスタムagent)は一致しています。

同じ分析では、遷移パターンごとにキャッシュの当たり方が次のように整理されています。毎回新規プロセスを起こす--printは、最終行のパターンを呼び出しのたびに踏みます。

遷移パターンキャッシュ理由
新規セッションの1ターン目→2ターン目キャッシュHIT理由同一プロセス内でmessages[0]が変わらない
新規セッション→再開(別プロセス)キャッシュMISS理由messages[0]が13.4KBから352Bへ激変する
再開後の1ターン目→2ターン目(同一プロセス)キャッシュHIT理由再開直後にmessages[0]が確定すれば、その後は安定する
再開→再開(--printのように毎回新規プロセス)キャッシュ部分的なHIT理由システムプロンプト分(約11k)だけ当たり、会話本体は毎回cache_create

プロンプトキャッシュそのものの基本的な仕組みと課金の考え方は、Claudeのプロンプトキャッシュの仕組みで解説しています。

自分の環境で症状を確認する方法

症状の確認は、stream-json形式の出力に含まれるcache_read_input_tokensとcache_creation_input_tokensを見るのが最も直接的です。

手順

症状を確認する3ステップ

  1. 1

    バージョンを見る

    claude --versionでv2.1.69〜v2.1.89の範囲にいるかを確かめます。v2.1.90で主要な修正が入っているため、それ以降なら症状は出にくくなります。

  2. 2

    同じセッションに3通以上送る

    下のコマンドで、同じセッションIDに3通以上のメッセージを送ります。1通目は新規でも再開でもキャッシュの書き込みが走るため、判断は2通目以降で行います。

  3. 3

    2つの数字の動きを比べる

    result出力内のcache_read_input_tokensが増えず、cache_creation_input_tokensだけが会話の長さに比例して伸びていれば、この回帰と同じ症状です。

claude --print --resume <session-id> --output-format stream-json --verbose

v2.1.251以降の/costにはPrompt cache (main)の行があり、要求数・キャッシュ経由の入力比率・ミス回数・warm/coldが出ます。v2.1.260以降は、直近のミスの推定原因がlikely cause: tool definitions changedのような形で添えられます。同じ数字はステータスラインのprompt_cacheフィールドからも読めます。ステータスラインでの具体的な表示方法はstatuslineでprompt_cacheを表示するにまとめています。

報告では、正常なv2.1.68でも1通目はcache_readが約14kでcache_createが約22.9kあり、コストは約$0.15でした。2通目以降はcache_createが4,636、879、802と縮み、$0.05、$0.03、$0.02へ下がっています。回帰後のv2.1.76は、1通目から5通目までcache_readが動かず、コストも下がりませんでした。2通目以降の下がり方が分かれ目です。

利用形態別の影響 — 新規プロセスでの再開ほど深刻

同じ--resumeでも、プロセスの起動パターンによって影響の大きさは変わります。issueのコメントで示された挙動の整理を、利用形態に当てはめると次のとおりです。

利用形態影響理由
ターミナルで--resumeして同一プロセス内で対話を続ける影響限定的(再開直後の1ターンのみ)理由再開直後だけmessages[0]が変わり、以降は同一プロセス内で安定してキャッシュが効く
claude --print --resumeを毎回新規プロセスから呼ぶ自動化(bot・CI等)影響大きい理由呼び出しごとに前回のキャッシュを使い回せず、非対称性を毎回踏む
アイドル時間が長く、セッションを空けてから--resumeする場合影響キャッシュのTTL切れとも重なりうる理由TTL経過による通常のキャッシュ切れと、この回帰の影響を切り分けにくい

セッションをアイドルのまま長時間放置した場合との切り分けや、TTL設計そのものの考え方はプロンプトキャッシュはClaude Codeの利用上限をどう軽くするかで扱っています。

修正はいつ入ったか

v2.1.69でのdeferred_tools_delta導入から、v2.1.90での公式修正までの流れは次のとおりです。

あゆみ

v2.1.69からv2.1.90までの経緯

  1. 〜v2.1.68正常

    会話が伸びるほどcache_readが増えます。

  2. v2.1.69deferred_tools_deltaを導入

    新規と再開でmessages[0]の内容が食い違い始めます。

  3. 2026-03-15issue #34629が報告される

    v2.1.76までの計測表と、v2.1.68への固定が添えられていました。

  4. 2026-03-30messages[0]の非対称性が特定される

    APIリクエストのキャプチャによる分析が、コメントとして投稿されました。

  5. 2026-04-01issueがクローズされる

    クローズ後に、v2.1.89でも未解決だというコメントが付きました。

  6. v2.1.90changelogに`--resume`の修正が載る

    対象はdeferred tools、MCPサーバー、カスタムagentのいずれかを使う環境です。

v2.1.90のchangelogには「Fixed --resume causing a full prompt-cache miss on the first request for users with deferred tools, MCP servers, or custom agents (regression since v2.1.69)」と明記されています。対象範囲(deferred tools・MCPサーバー・カスタムagent利用時)は、コミュニティが特定した原因と一致します。

同じ2026年3〜4月ごろには、1時間TTLが通知なく5分へ切り替わる別の回帰も見つかっています。原因も修正版も異なる独立した不具合で、詳細はClaude CodeのキャッシュTTLが1時間から5分に短縮された問題と対処法にまとめました。

修正後に残っていた注意点

v2.1.90は4月2日のコメントで「ほぼ直っているが、会話内で最初の1回の再開だけは、まだキャッシュを作り直すように見える」と報告されています。修正が主要な部分に効いている一方で、細部の確認が続いていたことになります。

4月9日には、別の投稿者が自作の補正ツールの検証として、v2.1.97でも再開時に案内ブロックがmessages[0]の外へ散らばると報告しました。ツール未適用の再開1通目で、cache_createが5,204、cache_readが20,939(80%)だったという数字です。公式の確認を経ていない第三者の計測で、補正ツールの宣伝を兼ねたコメントでもあるため、参考値として読むのが妥当です。v2.1.97のchangelogには、再開時のキャッシュミスを別途直したエントリもあります。issueは4月16日に、クローズ後の無活動を理由に自動でロックされました。

changelogには、再開時のキャッシュ崩れを別の原因で直したエントリがその後も並んでいます。

バージョン修正された再開時のキャッシュ崩れ
v2.1.97修正された再開時のキャッシュ崩れ添付メッセージがトランスクリプトに保存されず、再開時にキャッシュミスと途中入力の欠落が起きる
v2.1.248修正された再開時のキャッシュ崩れ利用超過に入ったアカウントで、ScheduleWakeupのツール定義が再開前後で変わり、再開1回目が全面ミスになる
v2.1.267修正された再開時のキャッシュ崩れclaude.aiコネクタのツールが再開前後で変わり、キャッシュのミスと拡張思考の欠落が起きる
v2.1.275修正された再開時のキャッシュ崩れ復元したメモリファイルの経過日数の注記が、圧縮や再開のあとで毎回変わる
v2.1.277修正された再開時のキャッシュ崩れSessionStartフックの出力で、/clear後の再開が最初のメッセージを一部失い全面ミスになる
v2.1.281修正された再開時のキャッシュ崩れ権限確認の保留中に再起動して再開すると、前回と違う履歴が送られる

--resumeのキャッシュは、messages[0]の食い違いだけで決まるわけではありません。再開前後でプレフィックスが1バイトでも変わる要因は、ほかにも複数あったことが分かります。

修正が公式に届くまでの間、issueのコメント欄では複数のユーザーがcli.jsを書き換える非公式パッチや、リクエストをフックしてmessages[0]を手動で組み直すNode.jsスクリプトを公開していました。バージョン間で内部の変数名が変わるため、こうした非公式パッチは対象バージョンが変わるたびに作り直しが必要という制約付きでした。

同じコメント欄では、修正前の暫定策として「--resumeのセッションを短く保ち、頻繁に新しいセッションIDへ切り替える」対処も挙げられていました。会話が伸びるほどcache_create分の再送コストが積み上がる構造だったため、セッションを短く区切ることで1回あたりの無駄を抑えるという考え方です。ただし、これは会話の連続性を犠牲にする応急対応です。v2.1.90以降は修正が入っているので、この対処が要るのは古いバージョンに留まる環境です。

v2.1.289の--helpで見る再開まわりのフラグ

ヘッドレスで再開する構成を組むときに関係するフラグを、v2.1.289のclaude --helpの出力から抜き出します(一部を省略しています)。

claude --version
# 2.1.289 (Claude Code)
 
claude --help
#   -p, --print                  Print response and exit (useful for pipes).
#   -r, --resume [value]         Resume a conversation by session ID, or
#                                open interactive picker with optional search term
#   --fork-session               When resuming, create a new session ID
#                                instead of reusing the original
#                                (use with --resume or --continue)
#   --no-session-persistence     Disable session persistence - sessions will not
#                                be saved to disk and cannot be resumed
#                                (only works with --print)
#   --output-format <format>     Output format (only works with --print):
#                                "text" (default), "json" (single result),
#                                or "stream-json" (realtime streaming)

--output-formatは--printとセットでしか効かないため、stream-jsonでキャッシュの数字を見る確認手順も--printが前提です。--fork-sessionは再開時に元のIDを使い回さず新しいセッションIDを作るフラグで、元のセッションを残したまま枝分かれさせたいときに向きます。--no-session-persistenceをつけたセッションは保存されないので、あとから--resumeで再開できません。

公式のCLIリファレンスには、claude -p --continueは-pやSDKで作ったセッションも対象にするが、--continue単独ではそれらを飛ばす、という記述もあります。同じ「続きから」でも、--continueと--resume <id>では拾うセッションの範囲が違います。

まとめ

v2.1.90のchangelogの修正が出ている以上、まずバージョンをそこまで上げることが前提になります。そのうえで、ヘッドレス再開のコストはcache_read_input_tokensが2通目以降に伸びるかで判断でき、伸びないときは/costの推定原因が次の手がかりになります。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. github.com/anthropics/claude-code/issues/34629
  2. github.com/anthropics/claude-code/blob/main/CHANGELOG.md
  3. code.claude.com/docs/en/costs
  4. code.claude.com/docs/en/cli-reference
この記事を共有:XはてブLinkedIn