bg daemon memory governor kills mid-flight workers on 16 GB Macs (gates spawns on os.freemem() under-read)

Open 💬 2 comments Opened Jul 5, 2026 by TuringQuiz

Summary

On a 16 GB Mac, the background-session daemon's memory governor kills mid-flight bg workers because it gates worker spawns on Node's os.freemem(), which chronically under-reads on macOS (it counts only genuinely-free pages and ignores the compressor's reclaimable memory). The result is a self-inflicted kill-storm: background sessions die when reactivated from Agent View, and some die before any user action while resuming mid-processing.

This is distinct from #63023 (workers dying on daemon self-restart/adopt after an auto-update). No update is involved here — it reproduces with a stable binary and idle updater.

Environment

  • macOS (Apple Silicon), 16 GB RAM
  • Claude Code 2.1.201 (daemon running continuously ~1d14h, no auto-update in the window)

What happens

Before spawning/claiming a bg worker, the daemon logs:

[bg] bg: low memory (NNMB free) — retiring settled workers before spawning <id>

and retires workers. In the churn it also kills workers that were just claimed/spawned and are still running:

[bg] bg claimed-spare 88006bfc (fleet)      # 13:18:02
[bg] bg settled 88006bfc (killed)            # 13:18:20  -> 18s later
[bg] bg spawned 5332df61 (fleet)             # 13:30:18
[bg] bg settled 5332df61 (killed)            # 13:30:32  -> 14s later

The NNMB free figure is os.freemem(). On this machine it reads ~79 MB while the OS is genuinely at 39% free (~6.4 GB), with zero jetsam events and 1.68 M pages sitting in the compressor. So the governor's trigger is essentially always tripped, and it fires on nearly every spawn (141 times over 2 days). With a warm fleet of ~12 bg sessions plus a spare pool, every fleet spawn/refill re-runs the governor.

At least once the governor fired while logging low memory (1565MB free) — retiring even with 1.5 GB reported free — suggesting the threshold is high and/or scaled by pending spawns.

Impact

  • Background/Agent-View sessions are repeatedly killed mid-run on 16 GB machines under normal multi-session use.
  • Kills hit brand-new sessions as readily as old ones — the death happens during the worker spawn, independent of session age or binary version, which makes it look like "random crashes even on fresh sessions."
  • Transcripts persist on disk and sessions resume with full context, so no data is lost — but the churn is disruptive and makes Agent View feel unstable.

Suspected root cause

The memory governor gates worker spawns on os.freemem(), which is not a valid signal for available memory on macOS (excludes compressor/cache/reclaimable). It should use compressor-aware / memory_pressure-based accounting (or macOS memorystatus levels) before retiring/killing workers.

Repro

  1. On a 16 GB Mac, keep ~10+ background sessions warm (Agent View / claude --bg).
  2. Reactivate sessions from Agent View over a period of time.
  3. Observe ~/.claude/daemon.log — frequent bg: low memory (…) — retiring settled workers before spawning lines and bg settled <id> (killed) within seconds of claimed-spare/spawned.
  4. Confirm it is not real pressure: node -e 'console.log(require("os").freemem()/1048576|0,"MB")' (~80 MB) vs memory_pressure | grep "free perc" (~39%) and log show --predicate 'eventMessage CONTAINS "jetsam"' --last 1d (empty).

Suggested fixes

  • Replace the os.freemem() gate with a compressor/pressure-aware check (e.g. memory_pressure, memorystatus/vm.memory_pressure), so the governor only retires when the OS is actually under pressure.
  • Never retire/kill a worker that was claimed/spawned within the last N seconds (protect in-flight workers from the retirement pass).
  • Consider scaling the warm-fleet / spare-pool size to available RAM to reduce spawn churn on low-RAM machines.

View original on GitHub ↗

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