Scheduled-task sessions never terminate — leaks ~48 headless processes/day, GBs of RAM

Open 💬 0 comments Opened Jul 6, 2026 by stevene

Environment: Claude Code 2.1.197 bundled with Claude Desktop (entrypoint: claude-desktop), macOS 26 (Darwin 25.2.0), Apple Silicon (M-series), 16 GB RAM.

Summary: Headless sessions spawned by the Claude Desktop scheduled-task runner never exit after completing their task. Each firing of a scheduled task leaves a permanently hung claude --output-format stream-json process and a stale entry in ~/.claude/sessions/.

Evidence (measured before cleanup):

  • A 30-minute cron task (defined in ~/.claude/scheduled-tasks/<name>/SKILL.md) leaked 105 processes over ~2.5 days — one per firing, uptimes at exact 30-minute multiples, session-file mtimes on :01/:31 marks.
  • Combined RSS ~8.5 GB (8482 MB → 717 MB after killing them; ~7.8 GB freed).
  • All 105 ignored SIGTERM and required SIGKILL — hard-hung, not draining.
  • Each leaves ~/.claude/sessions/<pid>.json ("kind":"interactive", "entrypoint":"claude-desktop", "nameSource":"derived"), so the session sidebar in both Claude Desktop and the Claude Code webview fills with dozens of dead entries.
  • Side effect: memory pressure destabilized concurrent interactive sessions on the same machine.

Steps to reproduce:

  1. In Claude Desktop, create a scheduled task on a 30-minute cadence (ends up under ~/.claude/scheduled-tasks/).
  2. Let it run for a few hours.
  3. Compare ps aux | grep 'output-format stream-json' and ls ~/.claude/sessions/ — one hung process + one session file per firing.

Expected behavior: The headless session exits when its turn completes, and its session-registry file is removed (or at least excluded from the session sidebar).

Workaround used: built a kill list from the session JSONs filtered by the scheduled task's cwd and process age > 45 min (to protect interactive windows), SIGTERM → SIGKILL each pid + its wrapper parent, then removed the session files of confirmed-dead pids.

View original on GitHub ↗