[BUG] Claude Desktop (Windows) silently kills run_in_background tasks after 15-min idle — WarmLifecycle taskkills the embedded CLI process tree

Status Open
Reported on v2.1.170
Maintainer reply None cached
Activity 7 comments · opened Jun 15, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

On Claude Desktop for Windows (build 1.12603.1, bundled Claude Code agent 2.1.170), any task started with run_in_background is silently killed after the session has been idle (tab/window hidden) for ~15 minutes. No error is shown, and the Desktop background-task list still reports the task as running. Only tasks started inside Desktop are affected — the same task started from the standalone CLI (~/.local/bin/claude, v2.1.177) or via OS-level scheduling survives.

Root cause (traced in …\Claude_1.12603.1…\app\resources\app.asar): the Desktop wraps the embedded CLI in a WarmLifecycle manager instantiated with idleTimeoutMs: 900*1e3 (15 min), timeoutOnHidden: true, armOnTurnComplete: "when-hidden". When the session tab is hidden and the idle timer fires, it runs:

onDisconnect → pauseSession → teardownSession(id, "pause") → teardownQuery → query.close()

On Windows, query.close() terminates the spawned claude.exe via taskkill /pid <pid> /T /F. The /T flag kills the entire process tree, so the CLI's run_in_background child processes are force-killed along with it. Re-interacting calls warmSession, which spawns a fresh CLI with a new PID (the PID change confirms this). Because teardownQuery only nulls the async handles (activeWorkflows, pendingGitBashIds, …) without reconciling task state, the background-task list keeps showing running (related: #18390, #59962).

This is a design behavior of Desktop's idle session-recycling, not a transient crash. macOS is likely affected too via the process.kill(-pid, "SIGTERM") branch (process-group kill).

What Should Happen?

Idle session recycling should not silently destroy work the user explicitly backgrounded. Either:

  1. Don't tear down the CLI process tree while it has live run_in_background tasks, or
  2. Detach run_in_background children from the CLI process group so they survive a WarmLifecycle pause/re-warm, and/or
  3. At minimum, when the process tree is killed, reconcile background-task state to failed/killed instead of leaving it stuck at running, and surface a notification to the user.

Error Messages/Logs

No error is produced — the silent kill IS the bug. Relevant Desktop-side evidence:

Analytics/log event emitted on the idle path:
  desktop_ccd_session_idle_paused { trigger: "idle_timeout", idle_timeout_ms: 900000, ... }

Decompiled code (app.asar, build 1.12603.1):
  new eWA({ idleTimeoutMs: 900*1e3, timeoutOnHidden: true,
            onDisconnect: t => this.pauseSession(t),
            onWarmUp: t => this.warmSession(t), ... })

  scheduleIdleTimeout(e,t){ e.idleTimeoutId = setTimeout(async()=>{
      if(this.config.armOnTurnComplete==="when-hidden" && e.isTabVisible) return;
      ... // → onDisconnect → pauseSession
  }, t) }

  teardownSession(e,t){ ... if(s.query){ this.teardownQuery(s); if(t==="pause"){ ...emit "paused" } } }

  teardownQuery(e){ try{ e.query?.close() }catch{} ; e.activeWorkflows=void 0; e.pendingGitBashIds=void 0; ... }

  // Windows process teardown inside query.close():
  process.platform==="win32"
    ? spawn("taskkill",["/pid",String(pid),"/T","/F"],{windowsHide:true})
    : process.kill(-pid,"SIGTERM")

Steps to Reproduce

  1. Open Claude Desktop for Windows (build 1.12603.1), start a Code session in any project.
  2. Have Claude start a long-running background task, e.g.:

run_in_background a loop that appends a timestamp to a log file every 10s
(PowerShell: while($true){ (Get-Date).ToString('o') | Out-File -Append .\timer.log; Start-Sleep 10 }).

  1. Note the PID of the spawned claude.exe and its child shell process in Task Manager.
  2. Switch to another app so the Claude Desktop session tab/window is hidden; leave it idle.
  3. After ~15 minutes of idle (hidden), observe:
  • The background task's child process is gone from Task Manager; the claude.exe PID has changed.
  • timer.log stops growing — no error anywhere.
  • The Desktop background-task list still shows the task as "running".

Control: run the exact same task from the standalone CLI (claude in a terminal) or via Windows Task Scheduler → it keeps running indefinitely. Only the Desktop-spawned path is affected.

Claude Model

Not sure / Multiple models

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

Claude Desktop 1.12603.1 (Windows); bundled Claude Code agent 2.1.170.

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

Other

Additional Information

_No response_

View original on GitHub ↗

4 Comments

github-actions[bot] · 2 months ago

Found 1 possible duplicate issue:

  1. https://github.com/anthropics/claude-code/issues/64433

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

raistlin7447 · 2 months ago

This would be nice to get fixed as it is very frustrating.

Jimgitsit · 1 month ago

Happened to me on MacOS CLI

Environment: Claude Code CLI 2.1.205, macOS 26.5.1 (Darwin 25.5.0), plain terminal (not the Desktop app).

I had two run_in_background tasks running (a Vite dev server and a long-running poller). After being away ~90 min, both were killed with SIGTERM (Terminated: 15). A couple of details from what's described above, in case they're useful:

  • It was the standalone CLI rather than Desktop.
  • Not silent, and it fired on my return rather than during idle: the tasks were killed right in front of me when I came back — the session surfaced kill notifications rather than leaving them stuck at "running." Might have been when I gave it focus.
Jimgitsit · 1 month ago

Follow-up on the standalone-CLI case above — and on the root cause.

Same environment as my earlier comment (Claude Code CLI on macOS, plain terminal).

The root issue is the lifecycle-initiated kill resting on a false assumption. The harness reaps run_in_background tasks after idle because it assumes a lingering process is abandoned and safe to clean up. But the harness has no way to know that. Plenty of background tasks — a dev server, a file watcher, a poller — are supposed to keep running precisely while the user steps away. Treating "idle" as "these processes are garbage" is wrong, and it kills exactly the long-lived tasks people start background jobs for.

On top of that, the kill spins up an unprompted, token-burning loop:

  1. The task is reaped with SIGTERM (Terminated: 15) after idle.
  2. The killed notification auto-re-invokes the agent — a full turn the user never asked for.
  3. The notification carries no reason (see #45781), so the agent can't tell "idle reaper" from "crash" and reasonably relaunches.
  4. The relaunch is reaped on the next idle→return → another notification → another turn → another relaunch…

Each iteration is a full agent turn, minutes apart, so the prompt cache is cold and the entire context is re-read uncached every time. The user is spending money, while away, on the harness fighting its own reaper.

What would fix it, in order of preference:

  1. Stop reaping. Don't kill background tasks on idle — the user (or the task itself) ends them. This is the correct default; a process running is not evidence it should be stopped.
  2. If some cleanup policy is truly required, it must not cost unprompted tokens. Auto-invoking the agent to "decide what to clean up" just relocates the same waste — it's spending the user's tokens with no prompt, exactly like the current bug. Any decision has to be cheap/deterministic in the harness, or deferred to the user's next real turn — never a fresh unsolicited agent invocation.
  3. At absolute minimum, put the reason in the killed notification (#45781) so the agent doesn't treat a lifecycle kill as a crash and relaunch into the loop.

The through-line: the harness should not terminate work it can't prove is abandoned, and it should never spend the user's tokens on activity the user didn't initiate.

Showing cached comments. Read the full discussion on GitHub ↗