Observed behavior
When a session contains an assistant message whose time.completed is permanently null (e.g. due to the process being killed mid-response), all subsequent user messages display the QUEUED badge permanently while the session is idle.
When the assistant starts generating a new response, the QUEUED badges disappear (because pending now points to the new in-progress message). Once the response completes, the badges reappear.
Restarting OpenCode does not fix the issue — the orphaned message persists in the database.
Root cause
In packages/opencode/src/cli/cmd/tui/routes/session/index.tsx:
const pending = createMemo(() => {
return messages().findLast((x) => x.role === "assistant" && !x.time.completed)?.id
})
const queued = createMemo(() => props.pending && props.message.id > props.pending)
pending finds the last assistant message with no time.completed. If that message is an orphan (created but never completed, no error recorded), pending permanently points to it. Every user message with a higher ID than this orphan is then marked as queued.
Steps to reproduce
- Start a session and exchange a few messages
- Kill the OpenCode process while the assistant is mid-response (e.g.
taskkill /f /im opencode.exe or the process crashes)
- Restart OpenCode and resume the same session
- Continue chatting — all new user messages display the QUEUED badge when the assistant is idle
Database evidence
The orphaned message in my session:
id: msg_cd639a895002ewT3VGAPtxafso
role: assistant
time: {"created": 1773120628885} ← no "completed" field
finish: null
error: null
All other assistant messages in the session have time.completed set.
Suggested fix
A few options:
-
Startup/session-load cleanup: When loading a session, scan for assistant messages with time.completed = null and error = null that are not the very last message. Set their time.completed to their time.created (or mark them with an error like OrphanedMessageError).
-
Smarter pending logic: Instead of scanning all messages, only consider the very last assistant message. An orphaned message buried in the middle of conversation history should not affect the queued state of later messages.
-
Graceful shutdown handling: On process exit/crash, finalize any in-progress assistant messages so they don't remain in a limbo state.
Environment
- OpenCode v1.2.21
- Windows 11 (cmd.exe shell)
- The crash was caused by running a sync script that kills OpenCode processes as part of its workflow
Observed behavior
When a session contains an assistant message whose
time.completedis permanently null (e.g. due to the process being killed mid-response), all subsequent user messages display the QUEUED badge permanently while the session is idle.When the assistant starts generating a new response, the QUEUED badges disappear (because
pendingnow points to the new in-progress message). Once the response completes, the badges reappear.Restarting OpenCode does not fix the issue — the orphaned message persists in the database.
Root cause
In
packages/opencode/src/cli/cmd/tui/routes/session/index.tsx:pendingfinds the last assistant message with notime.completed. If that message is an orphan (created but never completed, no error recorded),pendingpermanently points to it. Every user message with a higher ID than this orphan is then marked as queued.Steps to reproduce
taskkill /f /im opencode.exeor the process crashes)Database evidence
The orphaned message in my session:
All other assistant messages in the session have
time.completedset.Suggested fix
A few options:
Startup/session-load cleanup: When loading a session, scan for assistant messages with
time.completed = nullanderror = nullthat are not the very last message. Set theirtime.completedto theirtime.created(or mark them with an error likeOrphanedMessageError).Smarter
pendinglogic: Instead of scanning all messages, only consider the very last assistant message. An orphaned message buried in the middle of conversation history should not affect the queued state of later messages.Graceful shutdown handling: On process exit/crash, finalize any in-progress assistant messages so they don't remain in a limbo state.
Environment