What happened
The question tool ("ask user a question, await answer") is being silently cancelled mid-stream by the renderer's own auto-heal machinery, surfaced to the user as "Question cancelled before the user answered it." The user did not press Escape, did not click Stop, did not undo. The question-recovery-clock (introduced in #430 to recover hidden question blockers) decided the question state was lost, retried reverify three times, then escalated to halt(sessionID) — which routes through sdk.client.session.abort({mode: "hard"}) → server SessionPrompt.cancel (hard mode). The awaiting-question guard at packages/opencode/src/session/prompt.ts:325 only protects mode === "soft", so hard mode kills the very question the recovery clock was meant to recover.
This bug class has been "fixed" at least 5 times in the last weeks (commits e97d08cdf, 563d689d3, 2837fabbb, 2b7cdc070 / 70488eed0, dc1e9b82c, 54c7661d2, cb5c30d57). Every fix added a new recovery / detection layer; none repaired the cancel signal semantics. #430 in particular introduced an auto-heal path whose terminal action is hard-cancel — i.e., the recovery mechanism became the new killer.
Area
harness (session mechanics + cancel API) + app (renderer-side wiring)
Impact
Breaks an important workflow. Silently destroys an in-flight assistant turn the user was actively engaged with. No retry / no resume — user has to type the whole prompt again.
Steps to reproduce
Race condition, not deterministic. Reproduction conditions in the captured session:
- Start a long-running assistant turn in build mode (gpt-5.5 xhigh).
- During the turn, the assistant calls the
question tool.
- The renderer's question-blocker snapshot transitions to
missingRunning (UI thinks session is running but cannot find the question blocker — root cause of this snapshot misjudgement is not fully traced yet, see "open question" below).
question-recovery-clock arms a 3 s timer, fires reverify up to MAX_RETRIES=3 times, fails, escalates to halt(sessionID).
- Server hard-cancels the assistant message. UI shows "Question cancelled before the user answered it."
What did you expect to happen
Silent automatic destruction of user work should be structurally impossible. An auto-heal mechanism is allowed to detect, warn, hydrate, refetch, or surface a manual "retry / discard" affordance to the user — but it must not be able to push the destructive button itself.
PawWork version
v2026.5.19
OS version
macOS 15.x (Darwin 25.4.0)
Can you reproduce it again?
Sometimes
Diagnostics
Evidence is captured in the session log at docs/debug-session-log/pawwork-session-nimble-meadow-2026-05-19-05-14-53-question-tool-cancel.json (local-only; not pushed). Cross-references:
- Lines 8422–8436 — server-side abort record:
source: "session.prompt.cancel", reason: "hard_cancel", mode: "hard", propagation_point: "session.prompt.loop.onInterrupt", via_ctx_abort: false.
- Lines 40060–40075 — renderer-side diagnostic for the same abort:
event.name: "session.action.abort", data.source: "autoHeal", mode: "hard", result: "aborted". Confirms the cancel was triggered by the auto-heal clock, not by user gesture.
Cited code paths (HEAD as of writing):
packages/app/src/pages/session/blockers/question-recovery-clock.ts:108-150 — escalation path that calls input.halt(sessionID) after MAX_RETRIES reverify failures, and also when reverify returns proceed: true.
packages/app/src/pages/session.tsx:142-158 — haltAbort / haltWithSnapshot route halt to sdk.client.session.abort({mode: "hard"}).
packages/opencode/src/server/instance/session.ts:480 — server session.abort route.
packages/opencode/src/session/prompt.ts:319-336 — SessionPrompt.cancel(mode); awaiting-question guard at line 325 only applies to soft mode.
packages/opencode/src/question/index.ts:291 — abort listener that rejects pending question with the user-visible message.
Other hard-cancel call sites that share the same mode: hard bypass (not the trigger this time, but same bug class):
packages/app/src/components/prompt-input/keydown.ts:133 — Escape key.
packages/app/src/pages/session/use-session-commands.tsx:239 — undo command.
packages/app/src/pages/session.tsx:144 / :154 — revert and the old autoHeal entry.
Open question (not blocking the fix)
Why did the snapshot transition to missingRunning for a question that the server says was correctly asked? This is the trigger condition, not the destructive action. The fix below makes the destructive action structurally impossible, so the snapshot misjudgement becomes a recoverable annoyance rather than a session-killer; root-causing the snapshot logic itself can be a follow-up.
What happened
The question tool ("ask user a question, await answer") is being silently cancelled mid-stream by the renderer's own auto-heal machinery, surfaced to the user as "Question cancelled before the user answered it." The user did not press Escape, did not click Stop, did not undo. The
question-recovery-clock(introduced in #430 to recover hidden question blockers) decided the question state was lost, retriedreverifythree times, then escalated tohalt(sessionID)— which routes throughsdk.client.session.abort({mode: "hard"})→ serverSessionPrompt.cancel(hard mode). The awaiting-question guard atpackages/opencode/src/session/prompt.ts:325only protectsmode === "soft", so hard mode kills the very question the recovery clock was meant to recover.This bug class has been "fixed" at least 5 times in the last weeks (commits
e97d08cdf,563d689d3,2837fabbb,2b7cdc070/70488eed0,dc1e9b82c,54c7661d2,cb5c30d57). Every fix added a new recovery / detection layer; none repaired the cancel signal semantics.#430in particular introduced an auto-heal path whose terminal action is hard-cancel — i.e., the recovery mechanism became the new killer.Area
harness (session mechanics + cancel API) + app (renderer-side wiring)
Impact
Breaks an important workflow. Silently destroys an in-flight assistant turn the user was actively engaged with. No retry / no resume — user has to type the whole prompt again.
Steps to reproduce
Race condition, not deterministic. Reproduction conditions in the captured session:
questiontool.missingRunning(UI thinks session is running but cannot find the question blocker — root cause of this snapshot misjudgement is not fully traced yet, see "open question" below).question-recovery-clockarms a 3 s timer, firesreverifyup toMAX_RETRIES=3times, fails, escalates tohalt(sessionID).What did you expect to happen
Silent automatic destruction of user work should be structurally impossible. An auto-heal mechanism is allowed to detect, warn, hydrate, refetch, or surface a manual "retry / discard" affordance to the user — but it must not be able to push the destructive button itself.
PawWork version
v2026.5.19
OS version
macOS 15.x (Darwin 25.4.0)
Can you reproduce it again?
Sometimes
Diagnostics
Evidence is captured in the session log at
docs/debug-session-log/pawwork-session-nimble-meadow-2026-05-19-05-14-53-question-tool-cancel.json(local-only; not pushed). Cross-references:source: "session.prompt.cancel",reason: "hard_cancel",mode: "hard",propagation_point: "session.prompt.loop.onInterrupt",via_ctx_abort: false.event.name: "session.action.abort",data.source: "autoHeal",mode: "hard",result: "aborted". Confirms the cancel was triggered by the auto-heal clock, not by user gesture.Cited code paths (HEAD as of writing):
packages/app/src/pages/session/blockers/question-recovery-clock.ts:108-150— escalation path that callsinput.halt(sessionID)afterMAX_RETRIESreverify failures, and also whenreverifyreturnsproceed: true.packages/app/src/pages/session.tsx:142-158—haltAbort/haltWithSnapshotroutehalttosdk.client.session.abort({mode: "hard"}).packages/opencode/src/server/instance/session.ts:480— serversession.abortroute.packages/opencode/src/session/prompt.ts:319-336—SessionPrompt.cancel(mode); awaiting-question guard at line 325 only applies to soft mode.packages/opencode/src/question/index.ts:291— abort listener that rejects pending question with the user-visible message.Other hard-cancel call sites that share the same
mode: hardbypass (not the trigger this time, but same bug class):packages/app/src/components/prompt-input/keydown.ts:133— Escape key.packages/app/src/pages/session/use-session-commands.tsx:239— undo command.packages/app/src/pages/session.tsx:144/:154— revert and the old autoHeal entry.Open question (not blocking the fix)
Why did the snapshot transition to
missingRunningfor a question that the server says was correctly asked? This is the trigger condition, not the destructive action. The fix below makes the destructive action structurally impossible, so the snapshot misjudgement becomes a recoverable annoyance rather than a session-killer; root-causing the snapshot logic itself can be a follow-up.