Claude Code WSL2の「low memory」誤検知でバックグラウンドタスクが止まる原因と対処
WSL2で空きメモリが十分でもバックグラウンドタスクが「low memory」で強制終了される原因と、環境変数による回避策をまとめます。
WSL2でClaude Codeを使っていると、free -mで20GB以上の空きがあるのに「was stopped because the system is running low on memory」と表示され、バックグラウンドタスクが強制終了することがあります。原因はメモリの実際の不足ではなく、Linuxカーネルの圧力通知の仕組みに絡む誤判定です。環境変数CLAUDE_CODE_DISABLE_BG_SHELL_PRESSURE_REAPを1に設定すれば止まります。
「low memory」でバックグラウンドタスクが止まる仕組み
Claude CodeはmacOSとLinuxで、OSが「深刻なメモリ圧力」を報告し、かつセッションが30分以上アイドルで、ターンもサブエージェントも動いていないときに、実行中のバックグラウンドタスクを止めます。v2.1.193以降の挙動です。Windowsにはメモリ圧力を伝えるOSシグナル自体が無いため、この仕組みは働きません。
止められたタスクには「was stopped because the system is running low on memory」という文言が付きます。デバッグログには、タスクが止められた理由、または圧力イベントが来たのにタスクが生き残った理由が記録されます。
止まるのは条件がすべて揃ったときだけです。セッションが非対話、直近30分のうちにユーザー操作があった、メインループが動作中、サブエージェントやワークフロータスクが生きている、のいずれかに当てはまれば対象外になります。サブエージェントが所有するバックグラウンドコマンドはそもそも時間制限がありません。v2.1.218より前は、Ctrl+Bで手動でバックグラウンドへ回したコマンドは、このメモリ圧力による自動停止とも旧来の60分制限とも無関係に動き続けていました。
空きメモリが十分でも誤判定になる理由
GitHub issue #92448には、この誤判定を報告する3件が集まっています。最初の報告は空き26.8GB・利用可能29.2GBのWSL2、2件目はMemAvailableが45.9GBあったWSL2、3件目は48GB搭載のVPSです。いずれもdmesgにカーネルのOOM killerのログは残っていません。
最初の報告では、同じPython/Radiance処理を6回試して3回がkillされ、残り3回は完了しました。誤判定は毎回起きるわけではありません。再現しない試行があっても、原因が無いとは言えない性質のバグです。
2026年9月15日のコメントで根本原因が特定されました。Claude Codeのバックグラウンドタスクはprocess.on("memoryPressure")を登録し、イベントが来るとタスクを止めます。このイベントを発火させているのはJavaScriptランタイムのBun(Claude Codeの実行基盤)です。BunはLinux上で/proc/pressure/memoryにPSI(Pressure Stall Information)のトリガーを書き込みます。/proc/pressure/memoryが使えない環境では、cgroupのmemory.pressureにフォールバックします。トリガーはバックグラウンドシェルが0本から1本に増えるたびに新しく作られます。セッションが始まるたびに作り直される仕組みです。
問題はカーネル側のトリガー生成にあります。非特権のトリガーは、生成時点までに積み上がった圧力の累計値を起点にします。そのため生成後最初の更新で、起動からの累計がそのまま閾値超えとして扱われます。WSL2上での実測では、閾値150ミリ秒に対して実際の圧力の伸びは0〜115マイクロ秒しかありませんでした。それでも新しく作られたトリガー6本のうち6本すべてが誤発火しています。空きメモリの値そのものは無関係です。MemFreeが79GBあっても起きます。
同じトリガーの生成と破棄を3回繰り返す追加検証でも、3回とも生成後最初の圧力更新で誤発火しました。実行タイミングやマシンの状態に依存せず、トリガーを生成する動作そのものに埋め込まれた不具合だと分かります。
Serenisoftは当初、残りわずかだったswap(4GB中295MiB)を疑い、リークしていたPostgresコンテナ55個を止めてswap使用量を下げたところkillが止まったと報告しました。本人も「短い観察期間の一例で確証ではない」と留保しています。後続のvizbyarcherの解析は、swap・MemFree・MemAvailableのいずれとも無関係にトリガーが誤発火することを実験で示しており、swapを増やす対策では根本的には直りません。
この誤判定かどうかを切り分ける
自分の環境がこの誤判定に当てはまるかは、次の3点で確認できます。
free -mまたは/proc/meminfoのMemAvailableが十分にある- 直前にバックグラウンドタスクが0本から1本に増えた直後に発生している
dmesgにカーネルのOOM killerのログが無い
claude --debugでセッションを起動しておくと、タスクが止まった理由がデバッグログに記録されます。理由がmemory_pressureで、かつ上の3点に当てはまるなら、実際のメモリ不足ではなくこの誤検知です。
元の報告では、killと同時にdmesgへ「WSL (368) ERROR: No buffer space available @telemetry.cpp:190」というWSL2のテレメトリエージェントのログも出ていました。これはWSL2内部プロセスの通信エラーで、カーネルのOOM killerとは無関係です。このログが出ていても、それ自体はメモリ不足の証拠にはなりません。
claude --debug今すぐ止める設定
環境変数CLAUDE_CODE_DISABLE_BG_SHELL_PRESSURE_REAPを1に設定すると、このメモリ圧力による強制終了だけを止められます。自分の全プロジェクトに適用するなら~/.claude/settings.json、そのプロジェクトのチーム全員に配るならリポジトリ直下の.claude/settings.json(コミットして共有します)のenvブロックに書きます。組織全体に強制する場合は管理者側のmanaged-settings.jsonを使います。
{
"env": {
"CLAUDE_CODE_DISABLE_BG_SHELL_PRESSURE_REAP": "1"
}
}そのセッションだけで試すなら、シェルでエクスポートしてからclaudeを起動します。
export CLAUDE_CODE_DISABLE_BG_SHELL_PRESSURE_REAP=1
claudeこの変数は圧力による強制終了だけを止めます。出力が5GBを超えたときの自動終了や、タイムアウト時にバックグラウンドへ回す挙動には影響しません。バックグラウンド機能そのものを丸ごと止めたい場合は、別の環境変数CLAUDE_CODE_DISABLE_BACKGROUND_TASKSを使います。ただしrun_in_backgroundもCtrl+Bもすべて使えなくなるため、影響範囲がまったく違います。
誤判定と本物のメモリ不足を混同しない
Claude Codeの「メモリ不足」は、複数の仕組みが別々に持っています。症状が似ていても原因は別なので、まとめて「メモリを増やせば直る」と判断すると回り道になります。
| 症状 | 実際の原因 | 対応 |
|---|---|---|
| 空きメモリが十分なのにバックグラウンドタスクが繰り返し止まる | 実際の原因PSIトリガーの誤発火(この記事の症状) | 対応CLAUDE_CODE_DISABLE_BG_SHELL_PRESSURE_REAP=1 |
claude install自体がKilledで失敗する(exit code 137) | 実際の原因カーネルのOOM killerによる実際のメモリ不足 | 対応スワップ追加かインスタンス拡張 |
| Bash・PowerShellの特定コマンドだけが落ちる | 実際の原因CLAUDE_CODE_TOOL_MEMORY_LIMITのcgroup上限 | 対応上限値を見直すか0で解除 |
インストール時のKilledはスワップを増やせば解決しますが、この記事が扱う誤判定はスワップを積んでも直りません。トリガーの生成タイミングに起因するバグのため、実際のメモリやスワップの余裕とは無関係に発生します。インストールに必要な空きメモリはおよそ512MBで、実行時にはさらに必要になります。原因を取り違えると、対策をいくら重ねても症状は消えません。cgroupでコマンド単位のメモリを制限するCLAUDE_CODE_TOOL_MEMORY_LIMITでBash・PowerShellのメモリを制限する、実際にメモリを使い切ってプロセスが落ちるClaude Codeのメモリリークで120GB超・OOM Killになる原因と対処法は、いずれも本記事のメモリ圧力による自動停止とは別の仕組みです。
修正されたバージョンと、まだ直っていない部分
この機能はv2.1.193で追加されました。同時にオフスイッチの環境変数も用意されています。当初は「軽度の圧力」でも30分アイドルで止まる仕様でした。v2.1.274で判定基準が「深刻な圧力」に絞られ、デバッグログに理由が残るよう改善されています。
| バージョン | 内容 |
|---|---|
| v2.1.193 | 内容メモリ圧力による自動停止機能を追加。同時にオフスイッチも用意 |
| v2.1.261 | 内容issue #92448の最初の報告(WSL2、空き26.8GB) |
| v2.1.263 | 内容Serenisoftの報告。cgroup・OOM killer・os.freemem()を実測で除外 |
| v2.1.268〜272 | 内容vizbyarcherがバイナリを解析し根本原因を特定 |
| v2.1.274 | 内容判定基準を「深刻な圧力のみ」に絞り、デバッグログに理由を追加 |
| v2.1.283 | 内容確認できた最新版。PSIトリガーが作り直される挙動自体への修正は見当たらない |
2026年9月15日の最新コメントでは、原因がカーネルのPSIトリガー生成にあることまで特定されました。修正案として、Bun側でのトリガー再利用や、Claude Code側での確認の追加が提案されています。確認できた最新のv2.1.283までのchangelogに、この作り直しの挙動そのものを名指しした修正の記載は見当たりません。v2.1.274の変更は判定の閾値を厳しくしただけで、トリガーが返す値そのものが生成直後に不当に大きくなる問題は解消していないと考えられます。WSL2でバックグラウンドタスクを多用する場合は、修正を待つより環境変数で止めておくほうが確実です。
まとめ
WSL2で空きメモリが十分にあるのにバックグラウンドタスクが「low memory」で止まる場合、原因はメモリ不足ではありません。新しいバックグラウンドシェルを起動するたびにカーネルのトリガーが作り直され、そのたびに誤発火する誤検知です。settings.jsonのenvにCLAUDE_CODE_DISABLE_BG_SHELL_PRESSURE_REAP: "1"を書いておけば、この強制終了だけを止められます。
対象はmacOSとLinux(WSL2を含む)のv2.1.193以降で、Windowsネイティブ環境ではこの停止自体が起きないため対処も不要です。インストール失敗のKilledやCLAUDE_CODE_TOOL_MEMORY_LIMITによるコマンド制限とは別の仕組みなので、症状を混同せずに切り分けてください。診断の3点(空きメモリの有無・直前のバックグラウンド起動タイミング・dmesgのOOMログ有無)を確認してから対処すれば、無駄な設定変更を避けられます。切り分けを飛ばして環境変数だけ設定すると、本物のメモリ不足を見逃す恐れもあります。