Reported
"I can't stop the loop for Heartfull."
The server accepted the stop. Nothing in the UI ever says so.
Live state of the run right now (f8ddc272, run 252061dc):
status: running
cancelRequested: true ← the stop WAS recorded
iteration: 32 / 50
startedAt: 2026-08-07T07:58:02Z
lastProgressAt: 2026-08-07T09:06:31Z
So requestCancel worked: agent_loop_runs.cancel_requested = 1. The API returns it — it is right
there in check_instance_loop's payload as cancelRequested.
No UI reads it. git grep cancelRequested across store/console/src and
agents/coder/web/src returns nothing.
The consequence, in LoopRunsSection.tsx:
- pressing Stop sets a transient local message —
"Stopping — the current step will finish first."
(:87), which is honest but is component state and vanishes on the next poll, re-render or
navigation;
- the row itself renders from
r.status === "running" alone (:108, :125), so it keeps showing
· running and keeps offering a live Stop button.
So: press Stop → a message flashes → the row still says running → press Stop again → same. The user
correctly concludes it does not work. The flag is set, re-set, and re-set; the UI is simply not
looking at it.
Why the wait is long enough to matter
Cancellation is cooperative by design and the reasoning is sound (agent-loop-store.ts:160-166):
the flag is read at the top of each iteration (workflows/agent-loop.ts:77) so the in-flight
step settles its spend, because killing mid-step strands a budget reservation against the shared
tree pool.
But the in-flight step is act-${iteration} — handing an instruction to a Coder that drives Claude
Code through a real task. That is minutes, not seconds. This run has been 6m45s past its last
recorded progress and is presumably still inside iteration 32.
A cooperative cancel with a multi-minute settling window is fine. A cooperative cancel with a
multi-minute window and no persistent acknowledgement is indistinguishable from a broken button.
Fix
- Render the pending state. When
cancelRequested && status === "running", show Stopping…
instead of running, and disable the Stop button. The data is already on the wire; this is a
read, not a new endpoint.
- Say what it is waiting for — "finishing the current step" — so a multi-minute wait reads as
the documented behaviour rather than a hang. work-report.ts:87 already composes this sentence
for the work report ("a cancel has been requested"); the console just never got it.
- Consider a second press meaning something. Once a cancel is pending, a further Stop could
offer a hard abort. Not obviously right — it reintroduces the stranded-reservation problem the
cooperative design exists to avoid — so this needs a decision, not just an implementation.
Also worth noting from the same data
Three of this instance's six runs died with WorkflowInternalError: Attempt failed due to internal workflows error, and two more with the $50.00 daily limit (#343). The run list is mostly failures.
Not this issue, but the same instance is the evidence for several open ones.
Reported
The server accepted the stop. Nothing in the UI ever says so.
Live state of the run right now (
f8ddc272, run252061dc):So
requestCancelworked:agent_loop_runs.cancel_requested = 1. The API returns it — it is rightthere in
check_instance_loop's payload ascancelRequested.No UI reads it.
git grep cancelRequestedacrossstore/console/srcandagents/coder/web/srcreturns nothing.The consequence, in
LoopRunsSection.tsx:"Stopping — the current step will finish first."(
:87), which is honest but is component state and vanishes on the next poll, re-render ornavigation;
r.status === "running"alone (:108,:125), so it keeps showing· running and keeps offering a live Stop button.
So: press Stop → a message flashes → the row still says running → press Stop again → same. The user
correctly concludes it does not work. The flag is set, re-set, and re-set; the UI is simply not
looking at it.
Why the wait is long enough to matter
Cancellation is cooperative by design and the reasoning is sound (
agent-loop-store.ts:160-166):the flag is read at the top of each iteration (
workflows/agent-loop.ts:77) so the in-flightstep settles its spend, because killing mid-step strands a budget reservation against the shared
tree pool.
But the in-flight step is
act-${iteration}— handing an instruction to a Coder that drives ClaudeCode through a real task. That is minutes, not seconds. This run has been 6m45s past its last
recorded progress and is presumably still inside iteration 32.
A cooperative cancel with a multi-minute settling window is fine. A cooperative cancel with a
multi-minute window and no persistent acknowledgement is indistinguishable from a broken button.
Fix
cancelRequested && status === "running", show Stopping…instead of running, and disable the Stop button. The data is already on the wire; this is a
read, not a new endpoint.
the documented behaviour rather than a hang.
work-report.ts:87already composes this sentencefor the work report ("a cancel has been requested"); the console just never got it.
offer a hard abort. Not obviously right — it reintroduces the stranded-reservation problem the
cooperative design exists to avoid — so this needs a decision, not just an implementation.
Also worth noting from the same data
Three of this instance's six runs died with
WorkflowInternalError: Attempt failed due to internal workflows error, and two more with the$50.00 daily limit(#343). The run list is mostly failures.Not this issue, but the same instance is the evidence for several open ones.