Follow-up to #529, which asked for classified records and got them. Now that they exist, the first
thing they show is that the largest class is unknown, and every member of it is the same
Cloudflare failure — the one nobody has ever traced. This is that trace.
Population
Across all 32 instances (80 autonomous runs, 30 failed — see #541 for the full distribution),
WorkflowInternalError is the largest live failure class after stuck:
The two message shapes are the same event — CF's internal-error wording differs by surface. That is
8 occurrences, and 3 of them are in the last 24 hours. Of the 4 coding:session rows that exist
since #529 shipped, 3 are this and all 3 classify as unknown.
It is not terminal — one run recorded its own death twice
The decisive evidence. Two error_log rows, eight minutes apart:
|
row A |
row B |
created_at |
2026-08-12 15:05:34 |
2026-08-12 15:13:35 |
runId / traceId |
82739cb6-… |
82739cb6-… (same) |
sessionId |
csess_2dd3124c-… |
csess_2dd3124c-… (same) |
elapsedMs |
10,619,147 |
11,100,612 |
node |
Sergeys-Mac-mini.local |
RLs-MacBook-Air |
instructionChars / paneChars |
45 / 216 |
0 / 0 |
phase |
s6-decide |
s6-decide |
elapsedMs is Date.now() - runStartedAt (coding-failure.ts:274) and the two differ by 481,465ms
against an observed wall-clock gap of 481,000ms — the same runStartedAt, i.e. one run, not two.
So the workflow's catch block (coding-session.ts:667-679) executed twice for a single run.
And it was not the end even then: the loop-run row for 82739cb6 was finally closed at 15:17:25Z
with a third, different reason — stuck not resolved in time. Neither failure record was terminal.
Why the record duplicates
recordCodingFailure is called directly in the catch, not inside step.do
(coding-session.ts:674-677). Anything outside step.do re-executes on every workflow attempt, so
each attempt writes a fresh row while the journalled step.do results are replayed.
Row B's 0 / 0 is the confirming signature. probe.saw(pane.pane) is at coding-session.ts:397,
inside capture, which is invoked through guard(runRetry, \s${n++}-snapshot`, capture)at line 447 — i.e. **inside**step.do. A replay gets the journalled result and never re-runs the callback, so paneCharsstays 0.probe.at(name)(line 281) *is* outside, which is whyphasestill readss6-decide`.
That directly falsifies the docblock #529 shipped, coding-failure.ts:186-192:
"Everything here is set from code OUTSIDE step.do, which a Workflow replay re-executes … So a
run resumed after an eviction still reports the phase and payload sizes it actually died at,
rather than the zeroes a step-journalled counter would give."
Production row B is exactly "the zeroes". The phase claim holds; the payload-size claim does not.
Three consequences
1. An infra event is reported to the owner as the objective failing.
coding-session.ts:671 wraps anything that is not isRunnerGone into `run error: ${message}`,
and closeDelegation turns that into outcome: failed. Two of the five loop-run rows carry
| Acts: pushed directly to the trunk origin main; pushed directly to the trunk origin main; … —
the run had already done real work and pushed it and was still reported as failed. Identical in
shape to #523, and identical in cost: he cannot tell a dead run from a finished one, so he retries.
2. It classifies as unknown, so it is indistinguishable from a genuine unexplained crash.
Walked through classifyCodingFailure (coding-failure.ts:129-162) with
"WorkflowInternalError: Attempt failed due to internal workflows error": not isRunnerGone, not
isRunnerUnreachable, no CEILING_MARKERS hit, isTransientInfraError returns false (it matches
only durable object reset / code was updated / object has been reset / reset because —
transient-error.ts:12-15), no credential marker, upstreamStatus null, no rate-limit / overrun /
stall marker, does not start with anthropic → falls through to at("unknown") at line 162.
Result: level: "error" (unknown is not in EXPLAINED, line 173-180) and retryable: null.
3. Nothing collapses, so any frequency count off list_errors over-reports.
collapseRepeat matches on exact message (error-log.ts:116-121). Every CF internal error
carries a distinct reference = <random id>, so no two ever collapse — all three rows show
repeat_count: 1. Combined with (1), row count ≠ run count: 3 rows here are 2 runs.
What to do — cheapest first
(a) Give it a class. Add workflow_internal to CodingFailureClass and match
attempt failed due to internal workflows error + internal error; reference =. Put it in
EXPLAINED so it is warn, and set retryable: true. One-line-ish, and it immediately makes
"is unknown shrinking?" a real question again — today unknown is 75% of the coding rows and
means nothing.
(b) Do not report it as the objective failing. Same argument #523 makes for the subrequest
ceiling: the run was cut off, its committed work is intact. It should read like the isRunnerGone
branch already does at coding-session.ts:671 — reported as itself, without the run error: prefix
that "reads as a crash". A run that pushed to main and then hit an attempt error is not failed.
(c) Strip the volatile reference out of the collapse key. Either normalise the message before
collapseRepeat or add a signature column. Otherwise every future CF internal error is a new row
and #522/#538's repeat machinery is inert for this class — which is the class that most needs a
count.
(d) Fix the coding-failure.ts:186-192 docblock, and either move probe.saw/probe.drove
outside the journalled callbacks or state plainly that payload sizes are absent on a replayed
attempt. As written it invites the next reader to treat paneChars: 0 as "the run read an empty
pane" rather than "this row is a replay" — which is a wrong diagnosis, not just a missing one.
Open question for the owner
Should the catch re-throw instead? If WorkflowInternalError were rethrown, CF's own retry
machinery would handle it and the run might genuinely resume rather than be declared dead. I would
not do this yet: recordCodingFailure, closeDelegation and the finally block all have
side effects that would then repeat per attempt, and #529's own comment
(coding-session.ts:667-668) exists because a run that vanished with no record was the worse
failure. (a)+(b) are safe and independent; the rethrow is a design change that wants its own ticket.
Acceptance criteria
classifyCodingFailure("WorkflowInternalError: Attempt failed due to internal workflows error")
and …("internal error; reference = abc123") both return class: "workflow_internal",
retryable: true, level: "warn".
- A loop run killed this way does not carry the
run error: prefix, and its stopReason is
distinguishable from a run whose objective genuinely failed.
- Two occurrences with different
reference ids collapse into one error_log row with
repeat_count: 2.
- A unit test feeds the exact production strings from the 8 occurrences above (as
coding-failure.test.ts already does for deadlineMessage()), so a reworded CF message fails the
test rather than silently degrading to unknown again.
Regression risk
- Moving
workflow_internal into EXPLAINED/warn hides it from an error-level triage view. That
is the intent, but it means (c) must land with it — otherwise the class becomes both quiet and
uncountable, which is worse than today.
- Changing the terminal
detail string touches closeDelegation's reason mapping
(coding-session.ts:194-200) and the board-card ok/fail choice (line 232). seed-drift-style
string assertions elsewhere may pin the old wording — grep -rn "run error:" before changing it.
- (c) risks over-collapsing if the normaliser is too aggressive: two genuinely different errors
merging into one row loses a real signal. Normalise only the reference = <id> token, nothing else.
Provenance
The 5 loop-run occurrences are reconstructed from agent_loop_runs.detail via
GET /v1/instances/:id/loop and predate #529 — they have no error_log rows and no phase or
payload data; that era is only readable from the run row and chat. The 3 error_log rows are
post-#529 (2026-08-12) and are where every structural claim above comes from. The duplicate-record
finding is measured. That Cloudflare retried the attempt (as opposed to some other double-entry
path) is inferred — it is the natural reading of "Attempt failed" plus the fresh-probe
signature, but I did not reproduce it, and CF's public Workflows API doc 404s on the
retry-semantics page.
Follow-up to #529, which asked for classified records and got them. Now that they exist, the first
thing they show is that the largest class is
unknown, and every member of it is the sameCloudflare failure — the one nobody has ever traced. This is that trace.
Population
Across all 32 instances (80 autonomous runs, 30 failed — see #541 for the full distribution),
WorkflowInternalErroris the largest live failure class afterstuck:run error: WorkflowInternalError: Attempt failed due to internal workflows error(2026-08-05 07:38Z FWS, 2026-08-07 03:37Z + 03:52Z Heartfull, 2026-08-11 01:28Z FAS, 01:29Z Heartfull)
error_logrows, post-[bug] A CODING_SESSION run that dies logs nothing — 0 logError calls where every peer workflow has 3-5, so a failed run exists only as a chat bubble #529:internal error; reference = <id>, all at phases6-decide,all on instance
a1d3522f(2026-08-12 15:05:34Z, 15:13:35Z, 15:21:19Z)The two message shapes are the same event — CF's internal-error wording differs by surface. That is
8 occurrences, and 3 of them are in the last 24 hours. Of the 4
coding:sessionrows that existsince #529 shipped, 3 are this and all 3 classify as
unknown.It is not terminal — one run recorded its own death twice
The decisive evidence. Two
error_logrows, eight minutes apart:created_atrunId/traceId82739cb6-…82739cb6-…(same)sessionIdcsess_2dd3124c-…csess_2dd3124c-…(same)elapsedMsnodeSergeys-Mac-mini.localRLs-MacBook-AirinstructionChars/paneCharsphases6-decides6-decideelapsedMsisDate.now() - runStartedAt(coding-failure.ts:274) and the two differ by 481,465msagainst an observed wall-clock gap of 481,000ms — the same
runStartedAt, i.e. one run, not two.So the workflow's
catchblock (coding-session.ts:667-679) executed twice for a single run.And it was not the end even then: the loop-run row for
82739cb6was finally closed at 15:17:25Zwith a third, different reason —
stuck not resolved in time. Neither failure record was terminal.Why the record duplicates
recordCodingFailureis called directly in the catch, not insidestep.do(
coding-session.ts:674-677). Anything outsidestep.dore-executes on every workflow attempt, soeach attempt writes a fresh row while the journalled
step.doresults are replayed.Row B's
0 / 0is the confirming signature.probe.saw(pane.pane)is atcoding-session.ts:397,inside
capture, which is invoked throughguard(runRetry, \s${n++}-snapshot`, capture)at line 447 — i.e. **inside**step.do. A replay gets the journalled result and never re-runs the callback, sopaneCharsstays 0.probe.at(name)(line 281) *is* outside, which is whyphasestill readss6-decide`.That directly falsifies the docblock #529 shipped,
coding-failure.ts:186-192:Production row B is exactly "the zeroes". The phase claim holds; the payload-size claim does not.
Three consequences
1. An infra event is reported to the owner as the objective failing.
coding-session.ts:671wraps anything that is notisRunnerGoneinto`run error: ${message}`,and
closeDelegationturns that intooutcome: failed. Two of the five loop-run rows carry| Acts: pushed directly to the trunk origin main; pushed directly to the trunk origin main; …—the run had already done real work and pushed it and was still reported as failed. Identical in
shape to #523, and identical in cost: he cannot tell a dead run from a finished one, so he retries.
2. It classifies as
unknown, so it is indistinguishable from a genuine unexplained crash.Walked through
classifyCodingFailure(coding-failure.ts:129-162) with"WorkflowInternalError: Attempt failed due to internal workflows error": notisRunnerGone, notisRunnerUnreachable, noCEILING_MARKERShit,isTransientInfraErrorreturns false (it matchesonly
durable object reset/code was updated/object has been reset/reset because—transient-error.ts:12-15), no credential marker,upstreamStatusnull, no rate-limit / overrun /stall marker, does not start with
anthropic→ falls through toat("unknown")at line 162.Result:
level: "error"(unknownis not inEXPLAINED, line 173-180) andretryable: null.3. Nothing collapses, so any frequency count off
list_errorsover-reports.collapseRepeatmatches on exactmessage(error-log.ts:116-121). Every CF internal errorcarries a distinct
reference = <random id>, so no two ever collapse — all three rows showrepeat_count: 1. Combined with (1), row count ≠ run count: 3 rows here are 2 runs.What to do — cheapest first
(a) Give it a class. Add
workflow_internaltoCodingFailureClassand matchattempt failed due to internal workflows error+internal error; reference =. Put it inEXPLAINEDso it iswarn, and setretryable: true. One-line-ish, and it immediately makes"is
unknownshrinking?" a real question again — todayunknownis 75% of the coding rows andmeans nothing.
(b) Do not report it as the objective failing. Same argument #523 makes for the subrequest
ceiling: the run was cut off, its committed work is intact. It should read like the
isRunnerGonebranch already does at
coding-session.ts:671— reported as itself, without therun error:prefixthat "reads as a crash". A run that pushed to main and then hit an attempt error is not
failed.(c) Strip the volatile
referenceout of the collapse key. Either normalise the message beforecollapseRepeator add asignaturecolumn. Otherwise every future CF internal error is a new rowand #522/#538's repeat machinery is inert for this class — which is the class that most needs a
count.
(d) Fix the
coding-failure.ts:186-192docblock, and either moveprobe.saw/probe.droveoutside the journalled callbacks or state plainly that payload sizes are absent on a replayed
attempt. As written it invites the next reader to treat
paneChars: 0as "the run read an emptypane" rather than "this row is a replay" — which is a wrong diagnosis, not just a missing one.
Open question for the owner
Should the catch re-throw instead? If
WorkflowInternalErrorwere rethrown, CF's own retrymachinery would handle it and the run might genuinely resume rather than be declared dead. I would
not do this yet:
recordCodingFailure,closeDelegationand thefinallyblock all haveside effects that would then repeat per attempt, and #529's own comment
(
coding-session.ts:667-668) exists because a run that vanished with no record was the worsefailure. (a)+(b) are safe and independent; the rethrow is a design change that wants its own ticket.
Acceptance criteria
classifyCodingFailure("WorkflowInternalError: Attempt failed due to internal workflows error")and
…("internal error; reference = abc123")both returnclass: "workflow_internal",retryable: true,level: "warn".run error:prefix, and itsstopReasonisdistinguishable from a run whose objective genuinely failed.
referenceids collapse into oneerror_logrow withrepeat_count: 2.coding-failure.test.tsalready does fordeadlineMessage()), so a reworded CF message fails thetest rather than silently degrading to
unknownagain.Regression risk
workflow_internalintoEXPLAINED/warnhides it from anerror-level triage view. Thatis the intent, but it means (c) must land with it — otherwise the class becomes both quiet and
uncountable, which is worse than today.
detailstring touchescloseDelegation's reason mapping(
coding-session.ts:194-200) and the board-card ok/fail choice (line 232).seed-drift-stylestring assertions elsewhere may pin the old wording —
grep -rn "run error:"before changing it.merging into one row loses a real signal. Normalise only the
reference = <id>token, nothing else.Provenance
The 5 loop-run occurrences are reconstructed from
agent_loop_runs.detailviaGET /v1/instances/:id/loopand predate #529 — they have noerror_logrows and no phase orpayload data; that era is only readable from the run row and chat. The 3
error_logrows arepost-#529 (2026-08-12) and are where every structural claim above comes from. The duplicate-record
finding is measured. That Cloudflare retried the attempt (as opposed to some other double-entry
path) is inferred — it is the natural reading of "Attempt failed" plus the fresh-probe
signature, but I did not reproduce it, and CF's public Workflows API doc 404s on the
retry-semantics page.