Repository navigation
Description
Problem
When an assistant turn is aborted before it emits any parts, OpenCode can persist a completed assistant message with MessageAbortedError and zero parts.
The backend already considers that turn finished, but the web UI has almost nothing to render for it, so the session can look blank or stuck even though it is no longer running.
This is a different shape from:
#17680stale thinking state after interruption/restart#15267teardown race creating an empty continuation turn
This issue is specifically about the persisted message shape:
- assistant message exists
time.completedis seterror.name = MessageAbortedErrorparts.length = 0
Concrete evidence
Live session investigated:
- session:
ses_30ad0ece4ffegDiKt3ugw5wurA - final assistant message:
msg_cf98d282b0019d4mnTo0zcQl7H
Persisted row state:
time.completed = 1773714305166(2026-03-16 19:25:05 PDT)error.name = MessageAbortedErrorerror.data.message = "The operation was aborted."part_count = 0
Additional checks on that session:
- orphan assistant rows with
time.completed IS NULL:0 - pending/running tool parts:
0
So the backend state is not "still running". It is a completed aborted shell.
Why this is bad
The current UI path already treats MessageAbortedError specially, but when the assistant turn has zero visible parts, there is no substantive assistant content to show.
That makes the turn look blank or stuck, even though the backend has already finalized it.
Also, the persisted aborted error currently loses provenance. "The operation was aborted." does not tell us whether the source was:
- server restart
- user cancel
- client disconnect
- timeout
- some other abort path
Proposed fix
1. Persist abort provenance
Extend MessageAbortedError payload to include an optional structured source, for example:
server_restartuser_cancelclient_disconnecttimeoutunknown
Populate it at the abort sites and in restart recovery.
2. Render zero-part aborted assistant turns explicitly in the web UI
If an assistant turn is:
- completed
- aborted (
MessageAbortedError) - and has zero visible parts
then render a compact interruption card/banner from the error payload instead of effectively showing an empty assistant shell.
3. Add a narrow invariant log
Log only when an assistant message is finalized with:
error != nulltime.completed != null- zero visible parts
This keeps diagnostics cheap and focused.
Acceptance criteria
- A completed aborted assistant message with zero parts no longer appears blank/stuck in the web UI.
- The UI shows a clear interrupted/aborted state for that turn.
MessageAbortedErrorcan carry structured provenance for future diagnosis.- Tests cover the zero-part aborted case.
Activity
fix(session): surface empty aborted turns (anomalyco#17895)
fix(ui): align empty abort visibility (anomalyco#17895)
dzianisv commentedon Mar 17, 2026
Fork and upstream work are now split cleanly:
PR #17909 covers the persisted/rendered empty-aborted-turn visibility issue so these sessions stop looking blank/stuck in the UI. The fork PR keeps the runtime retry scoped to empty aborts only.
fix(session): surface empty aborted turns (anomalyco#17895)
fix(ui): align empty abort visibility (anomalyco#17895)
github-actions commentedon Jul 3, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.