Background task killed with status 'killed' though no TaskStop was issued for it (possible stop-routing race with a prior stop of a similar task)

Status Open
Reported on v2.1.206
Maintainer reply None cached
Activity 5 comments · opened Jul 10, 2026

Summary

A background task was terminated by the harness with status "killed" although no TaskStop was ever issued for it. The only TaskStop in the session targeted a different, earlier task with a nearly identical command line, issued ~20 seconds before the victim task was launched. The kill cost a multi-hour benchmark run (terminated ~1 second after its child process spawned) plus the investigation time to establish that neither the user nor the agent stopped it.

Environment

  • Claude Code 2.1.206, Windows 11 Pro (10.0.26200)
  • PowerShell tool, run_in_background: true tasks
  • Times below are local, same session, 2026-07-09

Timeline

  1. 14:39:10 — background task A launched: pwsh -NoProfile -File <repo>\scripts\test-perf.ps1 -TimeoutSeconds 7800 # broad-ok. The script spawns a test binary via Start-Process + WaitForExit, redirecting its output to a log.
  2. ~14:39:30TaskStop(A) issued deliberately (to relaunch with a larger timeout). Returned success. A's process tree — the pwsh parent and its detached Start-Process child — terminated. Expected.
  3. 14:39:52–55 — task B launched: same script, -TimeoutSeconds 14400 (near-identical command string), preceded in the same tool call by a Get-Process/Stop-Process sweep of stale test processes (which ran before B's pwsh started, so it cannot have killed B).
  4. 14:39:55–56 — B's pwsh and its freshly-spawned Start-Process child terminated simultaneously: both output streams cut mid-line with no error text, and the script's own log lacks the end-stanza it writes on every surviving code path (file timestamps confirm all writes ceased at 14:39:55–56).
  5. The harness later delivered B's task-notification with status: killed / "was stopped" — though the session transcript contains no TaskStop for B. The user confirmed they did not stop it either.

Fingerprint calibration (why this is a harness stop, not a crash)

Deliberately reproduced a real TaskStop against the same script shape: the kill reaches the whole tree including the detached Start-Process child, both streams cut with no error output, no script end-stanza — byte-for-byte the same signature as B's death. A crash or OS-level kill would have surfaced as status: failed with an exit code; killed is the harness's classification for stops it executes.

Repro attempts (both failed to reproduce — the bug did not recur)

  1. Bare task: launch A (Start-Sleep 90; 'marker'), TaskStop(A), immediately launch B with the identical command line → B survived its full 90s and printed its marker.
  2. Child-fidelity: parent pwsh script that Start-Process-spawns a child and WaitForExits (mirroring the victim's shape), same stop-then-launch sequence with near-identical command lines → B's parent and child both survived (child's marker file written), while the stop of A correctly killed A's child.

So "a TaskStop kills the next similar task" is not deterministically true — the incident looks like a race in stop routing/attribution (a stop matched against the newer task record with a near-identical command string created ~20s later, or a stop record applied twice).

Corroborating anomaly

A PreToolUse hook that had injected additionalContext on every comparable launch earlier that day (3 prior launches of the same script) injected nothing on either of the two 14:39 launches — hook processing behaved differently in exactly that window.

Expected

TaskStop terminates only its target task; a task with no issued stop is never terminated by the harness, and never classified killed.

Actual

A task with no issued stop was terminated ~1s into a multi-hour run and reported as killed.

Session reference (for internal correlation)

session_01BcYXyXiUK3hXQtt8T95CtL

View original on GitHub ↗

3 Comments

briansboyd · 1 month ago

Recurrence, same session, ~5.5 hours later — and it changes the hypothesis.

A fresh background task (same script shape: pwsh wrapper → Start-Process test binary, run_in_background: true) was launched at 19:54:54 and terminated with status: killed with no TaskStop issued by anyone for ~6 hours — the misrouted-stop hypothesis from the original report cannot explain this one.

Facts for this occurrence:

  • Process verified alive and healthy at 20:44 (45 CPU-minutes accumulated — a full-throttle multi-core workload ~50 minutes in).
  • Dead by 21:26; kill window [20:44, 21:26], i.e. 60–90 minutes into an expected multi-hour run.
  • Windows Application/System event logs for the window: no crash records (no Event 1000/1001), no WER, no .NET errors, no resource-exhaustion events. 64GB RAM machine with 27GB free afterward. The process did not crash and was not OOM'd — the termination came through the harness's kill path again.
  • The session was idle during the kill window (no tool calls, no cron jobs — the one scheduled job had been deleted hours earlier).
  • One session-state difference vs. this session's five earlier long-running background tasks that all completed normally: a persistent Monitor task (a tail -F | grep on the run's output file) was armed ~20:40, minutes into the victim's run and shortly before the kill window opens. Possibly coincidental; noted for correlation.

Combined picture across both occurrences: two background tasks killed with status: killed, neither with a stop issued for it, one ~1s after child spawn (20s after an unrelated TaskStop), one 60–90 minutes into an idle-session run — no crash artifacts either time. Whatever the internal condition is, it is not stop-routing alone.

Workaround adopted: launching the long-running work fully detached (Start-Process from a foreground call, no harness task record) so the task layer has nothing to act on.

briansboyd · 1 month ago

To state the headline of the recurrence above unambiguously for triage: TaskStop was not involved.

In the second occurrence there was no TaskStop issued by anyone — user or agent — for ~6 hours on either side of the kill. There was no stop in the session to misroute. The harness terminated a background task and classified it killed with no stop of any kind in play.

That also retires the original report's misrouted-stop hypothesis as the primary explanation: it was our best fit for occurrence #1's timing (a deliberate stop of a near-identical command line ~20s before the victim launched), but occurrence #2 demonstrates the kill path fires without any stop involvement, so stop-misrouting is no longer necessary to explain either kill. Whatever condition triggers the harness's kill path, it is something other than TaskStop routing.

morelandreas · 1 month ago

I observed the same apparent failure mode on Linux with Claude Code 2.1.206.

Two separate tasks launched with run_in_background: true were reported as <status>killed</status> / “was stopped” after 17.3 seconds and 12.6 seconds. No TaskStop invocation appears anywhere in the parent session transcript. An identical third dispatch then continued running normally for more than 13 minutes.

This independently supports the updated conclusion above: the harness kill path can fire without any TaskStop being involved.

I found no evidence of a kernel OOM, cgroup resource limit, systemd-initiated kill, or another OS-level termination mechanism. The successful third attempt ran under the same parent process hierarchy and cgroup configuration.

Environment

  • Claude Code: 2.1.206
  • Codex CLI: 0.144.1
  • Platform: Linux
  • Session: Claude Code running inside tmux
  • Background workload: Claude Code Bash tool with run_in_background: true

Observed sequence

14:06:15.600Z background ID <BACKGROUND_TASK_ID_1>
14:06:32.855Z <status>killed</status>
Background command "Dispatch Codex high-effort job in background" was stopped
14:06:58.293Z background ID <BACKGROUND_TASK_ID_2>
14:07:10.881Z <status>killed</status>
Background command "Re-dispatch Codex high-effort job" was stopped

There is no TaskStop tool invocation in the same transcript.

A third, identical dispatch had the following direct parent process hierarchy:

claude PID <PID_1>
└─ bash <PID_2>
└─ node ... codex exec <PID_3>
└─ codex <PID_4>

That attempt was still running after more than 13 minutes of observation, and its output log continued to grow.

Resource and cgroup checks

The active third attempt was running inside:

/user.slice/user-1000.slice/user@1000.service/tmux-spawn-….scope

The relevant cgroup values were:

pids.max=36977
pids.current=96
pids.events=max 0
memory.max=max
memory.current=5012611072
memory.events=low 0;high 0;max 0;oom 0;oom_kill 0;oom_group_kill 0

Inherited process limits were:

Max processes 123259
Max open files 1048576
Max locked memory 4061667328

These limits were far from exhaustion.

Kernel and system journal

The kernel journal for the relevant time window returned no entries:

sudo -n journalctl -k \
--since '2026-07-10 14:05:00 UTC' \
--until '2026-07-10 14:08:00 UTC'
-- No entries --

The broader system journal contained no signal, kill, cgroup, OOM, or Codex process messages for the incident window.

The host had approximately 25 GiB of available memory.

Audit limitations

systemctl is-active auditd -> inactive
sudo auditctl -s -> command not found
/var/log/audit -> does not exist

There is therefore no audit trail that can retrospectively identify the sender of an external signal.

No evidence was found of an external pkill or signal from another process or session, but this cannot be conclusively reconstructed after the fact without auditd.

Current assessment

The strongest explanation is that Claude Code’s internal background-task supervisor or lifecycle handling stopped the jobs.

The following alternatives appear unlikely based on the available evidence:

  • Process-specific cgroup or ulimit exhaustion
  • Kernel or global OOM
  • systemd cgroup termination
  • Application crash
  • Prompt content or workload-specific failure

The fact that the third identical dispatch succeeded under the same parent process hierarchy and cgroup configuration argues against the prompt, Codex command, or application code being the primary cause.

Reproduction status

I have not reproduced this deterministically.

A shell-level test using &, nohup, or a detached process would not exercise Claude Code’s run_in_background supervisor and would therefore not test the same failure path.

No processes, services, worktrees, or repository files were modified during the investigation.

Showing cached comments. Read the full discussion on GitHub ↗