Claude Media
Claude Codeでfind.exeが残るWindowsの確認と後始末

Claude Codeでfind.exeが残るWindowsの確認と後始末

WindowsのClaude Codeでバックグラウンドタスクの終了後もGit Bashのfind.exeが動き続けるときの見分け方、PowerShellでの終了手順、find / を止めるhookの例をまとめます。

WindowsでClaude Codeを使っていると、作業が終わったはずなのにファンが回り続け、タスクマネージャーに find.exe が1コア分のCPUを使って並んでいることがあります。Claude Code側ではバックグラウンドタスクが「停止済み」と表示されているのに、です。

GitHubのissue #91523がこの症状の報告です。Git Bash(MSYS)経由で動かした find が、タスクの停止後も孫プロセスとして生き残ります。issueはopenのままで、以下では「残っていたときの見分け方と後始末」「新しく残さないための設定」の順にまとめます。

何が起きているのか

issueの報告では、サブエージェントが find / -iname "bats" -type f のような広い検索を3本走らせました。どれもフォアグラウンドの120秒を超えてバックグラウンドへ移され、その後Claude Codeはタスクを「killed」と記録しています。ところが C:\Program Files\Git\usr\bin\find.exe は生き残り、親プロセスはすでに存在しませんでした。3本で全体のCPU容量の約13.5%を使い、数時間そのまま動いたとあります。

コメントを読むと、残る経路は1つではありません。

報告された原因

find.exeが残る3つの経路

  • タイムアウトでバックグラウンドへ移る

    サブエージェントのコマンドが120秒を超えるとバックグラウンドに移りますが、完了通知は親の会話にしか届かないという報告があります。サブエージェントは待てずに終わり、コマンドだけが持ち主なしで動き続けます。

  • TaskStopで止めても子が残る

    tail -n 0 -F some.log | grep --line-buffered x を TaskStop で止めると、bashのラッパーだけが終了し、tail.exe と grep.exe が動き続けた、という報告があります。

  • npxなどのシェルスクリプト経由

    npx -y tsx loop.mts を TaskStop した場合、上位2つの bash.exe だけが終了し、npx のbash以下の6プロセスが生き残りました。直接 node loop.mjs を起動した場合は全体が終了しています。

3つ目の報告者は、ツリーの停止が親プロセスIDをたどって子孫を探しているらしく、Git Bash(MSYS)のexec処理で途中の中継プロセスが先に終わると、その先の子孫にたどり着けないと推測しています。推測なので、仕組みの断定ではありません。

Claude Codeの仕様として、フォアグラウンドのサブエージェントが起動したコマンドは、サブエージェントの実行が終わると止まると書かれています。それでも残ったという報告があるので、止まるのはラッパー側で、孫プロセスの find.exe までは届いていないと読めます。

残っているかを確認する

まず、残っているプロセスを探します。Git for Windowsの find.exe は usr\bin 配下にあるので、パスで絞ります。PowerShellで次を実行します。

Get-CimInstance Win32_Process -Filter "Name='find.exe'" |
  Select-Object ProcessId, ParentProcessId, CreationDate, CommandLine

見るのは3列です。

列見方
CommandLine見方find / -iname ... のようにClaudeが走らせた検索か。自分で実行したものは対象外
CreationDate見方数十分から数時間前に始まっていれば、置き去りの可能性が高い
ParentProcessId見方該当するプロセスが存在しなければ、親が先に終わっている

CPUの使用状況は次のコマンドで見られます。HandleCount は後述のレジストリ走査の影響を見るときに使います。

Get-Process find |
  Select-Object Id, CPU, HandleCount, StartTime, Path

注意点が1つあります。親のPIDが存在しないことだけで孤立と決めないでください。コメントには、Git Bashのexecでスタブのプロセスだけが先に終了し、子が生きたパイプラインを処理し続ける例が書かれています。親が見つからなくても、動作中のタスクの一部である場合があります。CommandLine と開始時刻で、いま走っているタスクのものでないことを確かめてから終了します。

終了させる

置き去りと判断できたら、PIDを指定して終了します。

Stop-Process -Id 39536 -Force

子孫ごと消したいときは taskkill のツリー指定を使います。

taskkill /T /F /PID 39536

ただし /T も親子関係をたどるため、先ほどの npx の例のように途中が切れていると、その先には届きません。報告者が挙げている手順は、コマンドに含まれる固有の文字列(スクリプト名や出力ファイル名)で Win32_Process.CommandLine を検索し、見つかったうち親が存在しない最上位のプロセスに taskkill /T /F をかける、というものです。

Get-CimInstance Win32_Process |
  Where-Object { $_.CommandLine -like '*loop.mts*' } |
  Select-Object ProcessId, ParentProcessId, Name, CommandLine

*loop.mts* の部分は自分の案件の文字列に置き換えます。終了後にCPUとファンが落ち着けば完了です。issueの報告者も、手動で終了した後に収まったと書いています。

find / が重いだけでは済まない理由

放置のコストはCPUだけではありません。コメントの1つに、find / -iname "contextlib.pyi" 2>/dev/null | grep -i basedpyright を孤立させたところ、3時間12分でハンドルが約309万個に達した例があります。大半はレジストリのキーのハンドルで、停止後はシステム全体で約21万個に戻ったとのことです。

この報告者は、Git Bashの / に /proc/registry が含まれ、MSYSのランタイムがそこでレジストリを公開しているため、走査するとキーのハンドルが解放されないまま増えると説明しています。計測では、/proc/registry/HKEY_CURRENT_USER の走査は13秒で約17万5千ハンドルに達し、通常のフォルダの走査は182ハンドルで終わりました。副作用として、新しく起動したPowerShellが固まる症状も書かれています。

つまり、Claudeに find / を打たせないこと自体が、いちばん効く予防になります。

再実行の前に残りを消す理由

CPUより厄介な副作用が、古いプロセスの書き込みです。コメントの1つでは、サブエージェントが600秒のフォアグラウンド実行でタイムアウトし、バックグラウンドへ移ったコマンドを TaskStop で止め、同じコマンドを再実行しました。TaskStop は成功と表示しましたが、元のプロセスは約25分生き残り、1コア分のCPUと最大0.9GB程度のメモリを使いました。しかも再実行した側と同じ --dump の出力ファイルに書き込んでいたため、後から終われば新しい結果を古い入力で上書きしかねなかった、と書かれています。

出力ファイルを持つ処理を止めて再実行したあとは、タスクの停止表示を信用せず、先ほどの CommandLine 検索で旧プロセスが消えているかを確かめてから結果を使うのが安全です。

PowerShellツールでも同じことが起きる

issueのコメントによると、この挙動はBashツール専用ではありません。PowerShellツールでも、サブエージェント内で & python.exe -c "import time; time.sleep(130)" を実行すると、120秒の制限で同じバックグラウンド移行のメッセージが返ったとのことです。先頭の長い sleep は検査で弾かれるため、再現にはPythonのような別プロセスの待機を使ったと書かれています。ツール公式の説明でも、PowerShellツールは同じタイムアウトの規則に従い、同じ2つの環境変数を読みます。

同じコメントの報告者は、PreToolUse hookの入力に agent_id / agent_type が含まれるときだけ、updatedInput でコマンドを timeout -k 5 110 bash -c '...' に書き換え、110秒で自己終了させる回避策を使っています。この方式で、サブエージェントには110秒で終了コード124が返り、バックグラウンドタスクは作られず、残るものもなかったと計測しています。updatedInput はコマンドそのものを置き換えるため、検査の対象も書き換え後のコマンドになります。

新しく残さないための設定

検索の範囲を決めておく

issueの提案の1つは、find / のような無制限の検索をやめ、範囲を絞った rg や rg --files を使うことです。CLAUDE.mdに「ファイル検索は作業フォルダ内に限る。ルートからの find は使わない」と書いておけば、サブエージェントにも指示が渡ります。

PreToolUse hookでfind / を止める

報告者の1人は、Bash|PowerShell に対するPreToolUse hookで、ルートやドライブ直下の再帰検索を拒否しています。hookの入力には agent_id が含まれ、サブエージェント内の呼び出しかどうかを区別できます。同じ考え方で、Bashツールの find / を拒否する最小の例は次のとおりです。形は一例で、検出パターンは環境に合わせて調整してください。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-find-root.sh",
            "args": []
          }
        ]
      }
    ]
  }
}
#!/usr/bin/env bash
# 標準入力のJSONに "find /" などルートの再帰検索が含まれていれば拒否
input=$(cat)
if echo "$input" | grep -Eq 'find +(/|[A-Za-z]:)( |\\|")'; then
  cat <<'JSON'
{"hookSpecificOutput":{"hookEventName":"PreToolUse",
 "permissionDecision":"deny",
 "permissionDecisionReason":"find / は禁止です。作業フォルダ内に絞って rg を使ってください"}}
JSON
fi
exit 0

deny を返すと、ツール呼び出し自体が実行されず、理由はClaudeに渡ります。PowerShellツールを使う環境では、matcherを Bash|PowerShell にして、同じ検査をPowerShell向けにも用意します。

自動でバックグラウンドへ移すのをやめる

CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1 を設定すると、バックグラウンドタスクの機能が丸ごと無効になります。run_in_background パラメーター、タイムアウト時の自動移行、Ctrl+Bのショートカットも含まれます。タイムアウトしたコマンドは、移行せずに停止します。

{
  "env": {
    "CLAUDE_CODE_DISABLE_BACKGROUND_TASKS": "1"
  }
}

issueの報告者は、この変数は粗すぎる回避策だと書いています。開発サーバーやウォッチビルドをバックグラウンドで動かせなくなるためです。長い検索が原因で残るのが主な悩みなら、hookで検索を止めるほうが影響を小さくできます。

待ち時間を延ばす場合の注意

BASH_DEFAULT_TIMEOUT_MS(既定120000ミリ秒)を大きくすれば、120秒で移行する頻度は下がります。ただし、遅い検索を長く待つだけで、孫プロセスが残る問題は変わりません。移行を避ける目的でだけ延ばしても、原因の解消にはなりません。

まとめ

置き去りの find.exe は、Get-CimInstance Win32_Process でコマンドラインと開始時刻を見れば見分けられます。終了は Stop-Process か taskkill で行い、npx のように途中が切れた場合は固有の文字列で探します。予防は、find / を打たせない設定が最も直接的です。

issueにはサブエージェントが所有するタスクを親から止められない点や、WindowsのJob Objectによるプロセス全体の終了を求める声もあります。修正が入るまでは、手動の後始末と範囲を絞った検索を併用する運用になります。同じGit Bashを使う環境のつまずきには、約8KBでコマンドが切れる問題や、WSL2でバックグラウンドタスクが止まる問題があります。

この記事を共有:XはてブLinkedIn