Scheduled-task session processes still leak on 2.1.237 — and each orphan spins ~10% of a core indefinitely (follow-up to #54626)
Follow-up to #54626 (closed 2026-06-16 as "will be fixed in the next release", locked 2026-08-19 with a note to file a new issue). Still reproducing two months and ~90 patch versions later, and with a symptom that thread never captured: the orphans are not idle — each one spins a core at ~10% for ever.
Environment
- Claude Code: 2.1.237 (bundled)
- Claude Desktop: 1.34493.1
- macOS 26.5.2 (25F84), iMac21,1, 16 GB RAM
- 9 scheduled tasks configured via the
scheduled-tasksMCP
Summary
Every session the desktop app spawns — including every scheduled-task run — leaves its claude process resident after the run completes. The app reports the session as not running, the transcript stops being written, and the process stays alive indefinitely, burning CPU in what looks like a spin loop rather than blocking on stdin.
After three days without a restart I had 24 orphaned session processes consuming ~2.7 GB RSS and ~234% CPU (2.3 cores) continuously, on a machine that was otherwise idle.
Evidence 1 — the orphans are scheduled-task runs, identifiable by start time
Process start times cluster on the exact scheduled trigger times across consecutive days, with a consistent ~3-second daily drift:
| PID | Started | Task cadence |
|---|---|---|
| 38706 | Sat Aug 22 05:04:42 | daily task A |
| 34638 | Sun Aug 23 05:04:45 | daily task A |
| 33718 | Mon Aug 24 05:04:48 | daily task A |
| 90184 | Sat Aug 22 07:07:25 | daily task B |
| 88616 | Sun Aug 23 07:07:28 | daily task B |
| 10907 | Mon Aug 24 07:07:31 | daily task B |
One process per fire, none reaped. Oldest survivor at the time of capture: Fri Aug 21 19:50:10 — just over 3 days.
Each is paired 1:1 with a disclaimer helper, matching the fingerprint reported in #54626:
4310 1 /Applications/Claude.app/Contents/MacOS/Claude
33717 4310 /Applications/Claude.app/Contents/Helpers/disclaimer -- .../claude
33718 33717 ~/Library/Application Support/Claude/claude-code/2.1.237/claude.app/Contents/MacOS/claude --output-format stream-json ...
Evidence 2 — each orphan spins a core (new; #54626 only covered RAM)
Sampling accumulated CPU time across a 6-second wall window, every one of the 24 processes advanced its CPU clock:
pid cputime(t0) cputime(t0+6s) delta
6940 95:20.69 95:21.34 0.65s
81234 105:47.34 105:47.92 0.58s
60953 22:28.78 22:29.21 0.43s
... (all 24 processes advanced 0.43–0.65s)
Sum of deltas: 14.06 CPU-seconds per 6 wall-seconds ≈ 234% of one core, sustained, with zero sessions running work.
The lifetime figures make the waste concrete: PID 81234 had burned 105 minutes of CPU while alive 2 days 17 hours, having done nothing since its scheduled run finished on day one. This is not a process parked on a blocking read — something is looping.
On a laptop this is a battery and thermal issue, not just a memory one.
Evidence 3 — the app agrees the sessions are finished
list_sessionsreportedisRunning: falsefor all of them.- No transcript in
~/.claude/projects/had been written for over an hour, except the one session I was actively using.
So nothing in the harness believes work is in flight; the processes are simply never asked to exit.
Evidence 4 — killing them is non-destructive, which suggests the fix is cheap
kill on all 24 freed 2.7 GB and 2.3 cores with no data loss whatsoever. The transcript is the durable state: when a killed session is reopened, the app relaunches it with --resume=<uuid> and the conversation continues intact. I verified this on a session killed an hour earlier — it came back with full history.
If the process can be terminated at any point without consequence, then exiting it when the scheduled run completes should be safe.
Reproduction
- Configure a scheduled task via the
scheduled-tasksMCP on any daily cadence. - Leave the desktop app running for several days without restarting it.
pgrep -x claude | wc -l— expect one additional process per fire, none reaped.- Sample CPU:
ps -o time= -p <pid>twice, 6 seconds apart, and observe the clock advancing on a process with no session running.
Workaround
A reaper on a 30-minute StartInterval launch agent. It maps each process to its session — via --resume=<uuid> in argv, or by matching process start time against the transcript's creation time — and kills only processes whose own transcript has been idle past a threshold, never touching the most recently active session:
<details>
<summary>reap-stale-claude-sessions.py</summary>
# Maps pid -> session transcript, reaps only sessions idle beyond --idle-hours.
procs = session_processes() # ps, filtered to .../claude-code/.../MacOS/claude
tx = transcripts() # ~/.claude/projects/*/<uuid>.jsonl -> (birth, mtime)
newest = max(tx, key=lambda u: tx[u][2])
for p in procs:
uuid = p["resume"] or match_by_birth_time(tx, p["started"], window=300)
if uuid and uuid != newest and now - tx[uuid][2] > idle_limit:
os.kill(p["pid"], signal.SIGTERM)
</details>
This is a bandage over a lifecycle bug, though — a scheduled run that finishes should exit on its own.
Impact
With 9 scheduled tasks on daily-or-faster cadences, this reaches ~2.3 cores and 2.7 GB within three days of uptime, and grows linearly until the app is restarted. Earlier reporters on #54626 hit worse — one reported (2026-05-28) a kernel-level Jetsam event on macOS in which the accumulation drove the machine out of memory and the kernel jettisoned ~1,000 processes, including system daemons.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗