Background tasks killed for "low memory" with 17.9 GB available (MemFree vs MemAvailable)
Summary
Background Bash tasks (run_in_background: true) are killed with "the system is running low on memory" on a machine with ~18 GB of memory available. The free column is near zero, but only because that memory is reclaimable page cache. This looks like a threshold reading MemFree where it should read MemAvailable.
Environment
- Claude Code 2.1.261
- Debian 12 (bookworm), kernel 6.12.30+, x86_64
- 23 GB RAM
What happened
Three consecutive background tasks were terminated with status: killed and the summary:
Background command "..." was stopped because the system is running low on memory
free -m, taken seconds after the third kill:
total used free shared buff/cache available
Mem: 23804 5913 849 101 18936 17891
available was 17.9 GB. free was 849 MB only because 18.9 GB sat in reclaimable page cache — ordinary Linux behaviour on a long-uptime host.
The killed tasks were not memory-hungry. Each was a poll loop of the shape:
until ! kill -0 "$PID" 2>/dev/null; do sleep 20; done
echo "done"
A few MB of RSS each. They were killed at roughly 5–20 minutes of lifetime while waiting on external processes.
A Monitor task running an equivalent loop was started immediately afterwards under the same conditions and was not killed, which is what suggests the kill decision is specific to the background-Bash path rather than a system-wide OOM event.
Expected
Either the tasks are not killed while MemAvailable is high, or the message reflects the actual pressure metric being used.
Why it matters beyond the memory
These background tasks are the standard idiom for "notify me when this long-running thing finishes" — the tool description recommends exactly the until loop above for a single completion notification.
Killing the watcher does not touch the process being watched. So the observable result is that the work continues while the notification silently disappears: the session is left waiting for a signal that will never arrive, and a killed watcher is indistinguishable from one still waiting. In my case all three watched processes were alive and healthy when probed directly afterwards.
Repro sketch
On a Linux host where MemFree is near zero and MemAvailable is high (any box with a large page cache — e.g. after sustained file I/O):
- Start a long-running external process.
- Launch a Bash tool call with
run_in_background: truerunninguntil ! kill -0 <pid>; do sleep 20; done. - Wait.
Observed 3/3 kills in one session; the Monitor-based equivalent survived.
Note
The MemFree-vs-MemAvailable explanation is inferred from the numbers, not from reading the implementation. The reproducible fact is the kill with 17.9 GB available.