Scheduled tasks leak their process pair: work completes with end_turn but the session never exits (~16 GB/day)

Status Open
Reported on v2.1.237
Maintainer reply None cached
Activity 0 comments · opened Aug 23, 2026

Summary

Every scheduled-task run leaks its process pair. The task's work completes normally and the turn ends cleanly, but neither the session process nor its disclaimer parent ever exits. They accumulate indefinitely.

On a machine with one hourly task plus ~10 daily ones this is ~36 leaked pairs/day, roughly 16 GB of resident memory per day, until something kills them manually.

Environment

  • macOS 15 (Darwin 25.3.0), Apple Silicon
  • Claude Code 2.1.237, launched by the desktop app's scheduled-task runner
  • Also observed on 2.1.234

What happens

  1. A scheduled task fires.
  2. It runs correctly — in my case a ~21 second run of a local Python script.
  3. The session emits its final assistant message with stop_reason: end_turn. The transcript's last write is in the same minute it started.
  4. The process does not exit. It sleeps indefinitely, holding ~220 MB.

Observed one still alive 29 hours after finishing a 21-second job.

Evidence

Every leaked run is a pair — a Contents/Helpers/disclaimer parent plus the claude child:

PID    PPID   ELAPSED     STAT  CPUTIME
37942  1145   03:56:31    S     0:00.00     <- disclaimer parent, zero CPU
37943  37942  03:56:31    S     2:33.42     <- session child

The child:

  • has no subprocesses (so it is not a local MCP server holding it open)
  • has fds 0/1/2 as unix sockets to the still-alive parent — neither end closes
  • holds 3 ESTABLISHED HTTPS connections to the API
  • state S, accumulating a small amount of CPU forever (event loop still ticking)

Suspect

I examined the transcripts of 33 leaked sessions spanning 8 different scheduled tasks. All 33 contain a bridge-session entry with lastSequenceNum: 0 — a cloud-side session opened, never used to carry a single message, and apparently never torn down:

{"type":"bridge-session","bridgeSessionId":"cse_...","lastSequenceNum":0,
 "ownerAccountUuid":"...","ownerOrganizationUuid":"..."}

That correlates 100% with the leak and would explain the three open HTTPS connections. I can't tell from outside whether the bridge is the cause or another symptom of the same teardown failure.

The queue is clean — enqueue then dequeue 59 ms apart, nothing pending.

Not the cause

Ruled out by measurement:

  • Not the task's script or config. Output is complete and correct every time.
  • Not local MCP servers. The leaked process has no children.
  • Not one specific task. 8 different tasks all leak; an hourly one just dominates the count.

Reproduction

Any scheduled task, on any schedule. Check afterwards for surviving processes carrying the runner's signature (--disallowedTools AskUserQuestion) whose elapsed time greatly exceeds the task's actual runtime.

I could not reproduce it by invoking the binary directly with the same argv — a manually launched session exits at "Not logged in" before reaching the state where the leak occurs, so the runner's own credentialing appears to be part of the path.

Impact

Silent and cumulative. Nothing errors, nothing logs, and every ordinary health check reports fine — the process is alive, its port responds, and it is using CPU, so anything testing for "is it running" or "is it idle" says healthy. It is only visible as memory pressure and rising load days later.

View original on GitHub ↗