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)
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: truetasks - Times below are local, same session, 2026-07-09
Timeline
- 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 viaStart-Process+WaitForExit, redirecting its output to a log. - ~14:39:30 —
TaskStop(A)issued deliberately (to relaunch with a larger timeout). Returned success. A's process tree — the pwsh parent and its detachedStart-Processchild — terminated. Expected. - 14:39:52–55 — task B launched: same script,
-TimeoutSeconds 14400(near-identical command string), preceded in the same tool call by aGet-Process/Stop-Processsweep of stale test processes (which ran before B's pwsh started, so it cannot have killed B). - 14:39:55–56 — B's pwsh and its freshly-spawned
Start-Processchild 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). - The harness later delivered B's task-notification with
status: killed/ "was stopped" — though the session transcript contains noTaskStopfor 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)
- 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. - Child-fidelity: parent pwsh script that
Start-Process-spawns a child andWaitForExits (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
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
Recurrence, same session, ~5.5 hours later — and it changes the hypothesis.
A fresh background task (same script shape: pwsh wrapper →
Start-Processtest binary,run_in_background: true) was launched at 19:54:54 and terminated withstatus: killedwith noTaskStopissued by anyone for ~6 hours — the misrouted-stop hypothesis from the original report cannot explain this one.Facts for this occurrence:
Monitortask (atail -F | grepon 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 unrelatedTaskStop), 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-Processfrom a foreground call, no harness task record) so the task layer has nothing to act on.To state the headline of the recurrence above unambiguously for triage:
TaskStopwas not involved.In the second occurrence there was no
TaskStopissued 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 itkilledwith 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
TaskStoprouting.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
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:
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.