Background tasks mass-killed by memory-pressure reaper on Linux hosts with low MemFree but high MemAvailable (PSI ~0)

Status Open
Maintainer reply None cached
Activity 2 comments · opened Jul 17, 2026

Summary

On a Linux server, Claude Code's background-shell memory-pressure reaper repeatedly kills all of a session's run_in_background Bash tasks simultaneously, even though the machine has ~39 GB actually available (MemAvailable) and kernel memory PSI is ~0. The box merely has chronically low MemFree (0.7–3 GB of 62 GB) because most RAM is page cache — normal for a busy server. The reaper's pressure signal appears to treat this steady state as memory pressure.

In one interactive session (v2.1.212) this fired 8 times over 4 hours, killing 14 background tasks — long-running subprocess dispatches and a Gradle test run — each event killing every live background task in the same second. Tasks die mid-stream with <status>killed</status> and the generic summary Background command "…" was stopped. Earlier sessions on the same host (07-14 → 07-16) show the same bursty signature (29 kill notifications total).

Environment

  • Claude Code v2.1.212 (native installer), interactive CLI session
  • Ubuntu, kernel 6.8.0-124-generic, x86_64, 12 CPU, 62 GB RAM
  • Long-lived session dispatching multiple concurrent background tasks

Evidence

Kill events (UTC, 2026-07-17): 17:59:11, 18:04:21, 19:46:09, 20:23:22 (×2 tasks), 20:40:56 (×2), 21:30:12 (×3), 21:43:28 (×2), 21:47:14 (×2). Task lifetimes at death ranged 30 s – 10 min (no timeout param was set; all tasks run_in_background: true).

Everything else ruled out:

  • No TaskStop tool call anywhere in any session transcript; user did not stop tasks from the UI.
  • Kills happened while the session was idle (last turn ended minutes earlier).
  • No session restart: single SessionStart hook entry, single version, same sessionId throughout.
  • journald/dmesg clean at the exact kill seconds — no OOM killer, no systemd-oomd events, no cron/timer activity.
  • Other concurrent Claude Code sessions' background tasks survived the same instants (consistent with a per-process event, not an external killer).
  • Child processes die ~1 s before the kill notification is enqueued into the transcript; their logs end mid-stream with no error.

Mechanism (from the shipped binary): each backgrounded task registers a listener on the runtime's memoryPressure event; on the event it emits task_local_shell_pressure_reap, marks the task killed, and kills the tree — so one pressure event wipes every running background task at once:

if (i===void 0 && !un() && !Z.CLAUDE_CODE_DISABLE_BG_SHELL_PRESSURE_REAP) {
  let a=()=>{ let l=r.get(e);
    if (l?.status!=="running"||l.notified||Date.now()-a$()<SJg||Vri()||iCt(r.all())) return;
    Ae("task_local_shell_pressure_reap"), dps(e,t,"killed",void 0,r,n,o,i), QOe(e,r) };
  process.on("memoryPressure", a); ...
}

Memory state at kill times (sar, 10-min samples around the first two events): MemFree ≈ 0.7–3 GB, MemAvailable ≈ 38–40 GB, %memused ≈ 36 %, buff/cache ≈ 31 GB. /proc/pressure/memory: some avg10=0.00 avg60=0.00 avg300=0.02. No OOM events in the journal — ever.

Expected behavior

The pressure signal should not fire on a host where MemAvailable is high and PSI is ~0. On Linux, low MemFree alone is not memory pressure — page cache is reclaimable. Suggest keying the reaper on MemAvailable and/or /proc/pressure/memory instead of free-memory, and including the reap reason in the task notification (the generic was stopped made this very hard to diagnose — it's indistinguishable from a user/agent stop).

Workaround

CLAUDE_CODE_DISABLE_BG_SHELL_PRESSURE_REAP=1 (set via the env block in settings.json).

Related

Possibly related idle/background-termination reports: #75037, #72851, #72171.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗