Claude Code を無人で回している。夜のうちに長い作業を任せて、朝に結果を見る。その運用にとって、いちばん質の悪い壊れ方がある。
落ちるのではない。エラーを吐くのでもない。誰も拒否していないのに、静かに止まって、待つ。落ちてくれれば気づく。エラーが出れば追える。でも「止まって待つ」は、正常な一時停止と見分けがつかない。夜通しのつもりが、最初の数分で止まって、朝まで何もしていなかった――これが起こりうる。
2.1.210 の更新履歴に、その正体が書いてあった。
Fixed a hook callback timeout being misreported to the model as a user
rejection, which made unattended sessions stop and wait
「hook のコールバックのタイムアウトが、モデルには『ユーザーによる却下』として誤って伝わっていた不具合を修正した。この誤報のせいで、無人のセッションが止まって待ってしまっていた」。ここは公式の更新履歴を出典とする伝聞で、手元で狙って再現したわけではない。手元の版は 2.1.202 で、この修正より前。だから、まだこの挙動が残る側にいる。
これを読んで、ぞっとした。うちは hook を大量に積んでいる。どれか一つの折り返しが遅れたら、却下と取り違えられて、夜中に静かに止まる。そう思って、自分の環境を数えた。
まず、数えた
自分の環境で数えたら、~/.claude/settings.json に登録された hook は、58個あった。そのうち、タイムアウトを明示しているものは、0個。全部が既定のタイムアウト頼みだった。ここから先は、実測――自分の環境で一つずつ開いて確かめた話になる。
数字だけ見れば、最悪だ。58個の折り返しが、どれも既定の上限に賭けている。だが、数だけでは何も分からない。効くのは数ではなく、遅さだ。タイムアウトの入口になるのは、折り返しが遅れる hook――ネットワークを叩くもの、重い集計をするもの、外部コマンドの完了を待つもの。だから、一つずつ中を開いた。
一つずつ、中を見た
最初に引っかかったのは、curl や wget や gh を含む hook が10個ほどあったことだ。ネットワークを叩くなら、これが筆頭の容疑者になる。
だが開いてみると、様子が違った。どれも、自分がネットワークを叩くのではなかった。「危ないネットワークのコマンドを止める」ために、実行しようとしているコマンドの文字列を照合しているだけ。秘密情報の持ち出しを止める hook、外部への送信を警告する hook。名前に curl が出てくるが、curl を実行するわけではない。文字列を見て、即座に通すか止めるかを決める。速い。
重い走査をする hook も探した。一つ、git の壊れた残骸を数える hook が find を使っていた。だが走査の範囲は .git/objects の下に限定されていて、家のディレクトリを丸ごと歩くようなことはしていない。これも速い。
結論。うちの58個は、全部が速いローカルの照合だった。ネットワークも、重い走査も、外部の待ちもない。だから、このバグへのうちの露出は、低い。
正直、拍子抜けした。だが、点検して初めてそう言える。数える前は、「58個・タイムアウト0」の字面に怯えていただけだった。
リスクは、バグ単体ではない
ここが要点だ。この壊れ方は、バグ単体では牙をむかない。バグ × 遅い hook、で初めて無人のセッションを止める。速い hook しか積んでいなければ、2.1.210 より前でも、この誤報の入口は踏みにくい。逆に、外部を叩く hook を timeout 無しで積んでいたら、そこが急所になる。
だから備えは、自分の hook の中身しだいで決まる。人ごとに違う。
じゃあ、どう備えるか
一つ、版を上げる。2.1.210 以降なら、更新履歴によればこの誤報は直っている。まずここが根本だ。
二つ、それとは別に、自分の hook を数えて、遅いものを名指しする。ネットワークを叩く、重い集計をする、外部の完了を待つ――心当たりがあれば、その hook に timeout で蓋をする。応答が来なければ既定の値で速やかに返す。折り返しが内部の上限を超えなければ、そもそも誤報の入口を踏まない。
無料の cc-safe-setup には、この周辺に効く例がある。長く止まりうるコマンドを時間の上限なしで走らせようとした時に警告する bash-timeout-guard.sh、無人で動かしている処理の死活を見張る dispatch-liveness-watchdog.sh。正直に書くと、この誤報そのものを止める hook は書けない。実行時の内部の誤報で、利用者の hook の側では割って入れない筋のものだからだ。だが、折り返しが遅れる元を減らすことと、止まった無人のセッションに気づくことは、手元でできる。
まとめ
落ちるより、黙って止まるほうが、無人の運用ではたちが悪い。正常な一時停止と、見分けがつかないからだ。2.1.210 より前の Claude Code は、hook の折り返しの遅れを、人による却下と取り違えて、無人のセッションを静かに止めていた(更新履歴に基づく)。
備えは、版を上げること。自分の hook を数えて、遅いものに timeout で蓋をすること。止まったことに気づける見張りを一枚置くこと。うちの58個は、幸い全部が速かった。でも、数えるまでは分からなかった。無人で回すなら、まず自分の足元を数えたほうがいい。
「消えた」「止まった」「壊れた」と見えた瞬間に、実際は何が起きているのか。報告と実態がずれる型――成功したと嘘をつく、却下と誤報する、消えたように見えて残っている――は、公開の起票で次々と報告されている。それを一つずつ一次検証して、症状ごとにまとめた本を書いている。『Claude Code 事故防止ハンドブック』(¥800・第3章まで無料)。無人で長く走らせる人向けの一冊だ。