Claude Desktop (macOS): scheduled-task session processes never exit after the run completes; WarmLifecycle disconnect loops forever and per_task_limit jams
Environment
- Claude Desktop for macOS, bundled Claude Code CLI 2.1.219 (
~/Library/Application Support/Claude/claude-code/2.1.219/claude.app/Contents/MacOS/claude, launched viaClaude.app/Contents/Helpers/disclaimer) - macOS Darwin 25.5.0, Mac mini (Apple silicon), Max plan
- Remote control auto-enable is ON (
[rcAutoEnable] verdict: enable=true source=explicit_pref)
What happens
Scheduled tasks configured in the Desktop app (CCDScheduledTasks) spawn a headless local session per run. The run itself completes fine (transcript shows the finished work and a custom-title event after ~3 minutes), but the CLI child process never exits. main.log then repeats forever, every 900 s, for days:
[WarmLifecycle:session] Idle timeout reached, disconnecting local_<id>
[WarmLifecycle:session] Starting idle timeout for local_<id>: 900s
One zombie per scheduled run accumulates. Observed: five processes over five days (2026-07-29 to 2026-08-02, one daily task at 06:58 plus one other task run), each ~140-160 MB RSS plus their stdio MCP server children, with steadily growing swap. On another Mac this same leak previously escalated to ~97 leaked session processes in a day (2026-07-09), which exhausted swap and made fresh claude spawns hang until the app was restarted.
A stuck session also permanently occupies the task's dispatch slot. Logged every minute, indefinitely:
[CCDScheduledTasks] Skipping dispatch for <task>: per_task_limit (active=1, limit=1)
so further runs of that task are silently skipped until the zombie is killed externally.
Spawn-to-zombie timeline (sanitized log excerpts)
06:58:34 [CCDScheduledTasks] Spawning new session for scheduled task morning-briefing { cronExpression: '50 6 * * *', ... }
06:58:39 Using Claude Code binary at: .../claude-code/2.1.219/claude.app/Contents/MacOS/claude
06:58:39 [WarmLifecycle:session] Starting idle timeout for local_<id>: 900s
06:58:39 [rcAutoEnable] verdict: enable=true source=explicit_pref
06:58:39 Enabling remote control for session local_<id>
06:58:58 [CCDScheduledTasks] Confirmed task run for: morning-briefing
07:01:xx (run completes; transcript stops)
07:13:39 [WarmLifecycle:session] Idle timeout reached, disconnecting local_<id>
07:13:39 [WarmLifecycle:session] Starting idle timeout for local_<id>: 900s
... repeats every 900 s for days ...
Every zombie had remote control enabled at spawn; a plausible mechanism is that the remote-control bridge registration keeps the WarmLifecycle disconnect from ever finalizing, so the process is retained for remote attach forever.
Expected
Scheduled-task session processes exit (or are reaped) once the run completes, and per_task_limit slots are released.
Confirmations
- Externally SIGTERMing a zombie is handled cleanly: the app logs
Claude Code process exited with code 143, and a jammed task dispatches its pending run within seconds. - The runs themselves are healthy; only process teardown fails.
Workaround
An external hourly watcher that SIGTERMs Desktop-CLI session processes (claude-code/<ver>/claude.app/Contents/MacOS/claude) older than 24 h with no controlling TTY.
Related minor issue
The task firing in these logs exists in the Desktop app's internal registry but is not returned by the scheduled-tasks MCP (list_scheduled_tasks) on the same machine, so it is invisible to tooling; the two registries appear to have diverged.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗