What happened
The composer keeps rendering a phantom queued-message card after the Runtime Host queue no longer holds that entry. Every attempt to delete it fails with not_found and a generic 操作失败 / 任务操作失败,请稍后重试。 toast, and the card never goes away.
Observed while sending the next message in a session that had a follow-up entry queued ("下一轮 · 每轮一条"):
ERROR Error occurred in handler for 'sessions:retractQueueEntry': RuntimeHostOperationError: Message queue entry does not exist
at #acceptResponse (@maka/runtime-host/dist/client/connection.js:414:24)
operation: 'queue.entry.retract',
code: 'not_found'
14 identical failures in ~4.3s (2026-09-20T04:03:14.700Z → 04:03:18.297Z, ~170ms apart), i.e. one renderer IPC call per click, with no main-process retry involved.
Expected
Either the card disappears as soon as the Host queue no longer holds the entry, or a retract of an entry the Host no longer has converges the local projection (already-settled / idempotent) instead of surfacing a generic failure.
How to reproduce
- In a session with a running Turn, submit a follow-up message (Enter = next turn). The composer's queue strip shows the card with 编辑 / 调整方向 / 删除.
- Leave that session (switch to another one, or let it sit) so the queued entry leaves the Host queue while no root Turn is live in the snapshot this renderer last observed — e.g. the follow-up is consumed and the Session goes idle, or a drain happens off-subscription.
- Come back to the session. The card is still rendered as queued, with 删除 enabled.
- Click 删除. Nothing is deleted: the toast
操作失败 / 任务操作失败,请稍后重试。 appears and the card stays. Clicking again repeats it indefinitely.
Intermittent by nature (it needs a queue change to land while the client is not observing a live Turn), but once the projection is stale it is stuck — reloading the renderer (Cmd+R) or restarting the app clears it, switching sessions back and forth does not.
Environment
- Maka: 0.2.0-dev.38.20260916 — packaged, nightly channel
- Surface: Desktop (macOS, arm64)
- Electron 43.4.1 · Chrome 150.0.7871.224 · Node 24.18.1 · darwin 25.4.0
- Locale: zh-CN
- Runtime Host: protocol v0, compatibility 157, epoch
b3572bc0-cf03-44fa-ab94-fde952e24a6f, healthy ("ready", 1 connection)
- Long-lived process: main-process and Runtime Host uptime ~161,200s (~45h) at capture time
Logs, screenshots, or additional context
Analysis (line references from eb23ac92)
The failing operation is the renderer asking the Host to delete a queue entry the Host no longer has — packages/runtime-host/src/server/message-coordinator.ts:1766 (#retractQueuedEntryAdmitted): a missing entryId returns exactly this not_found; an in-flight entry would return operation_conflict, an archived Session session_archived, neither of which appears here.
The card is stale local state, not Host state:
- The Desktop queue projection is driven only by
queue_update events — apps/desktop/src/renderer/app-shell-session-events.ts:306 (messageQueueBySession); there is no snapshot fallback.
- The Host only pushes
queue_update when the queue actually changed and a root Turn exists: packages/runtime-host/src/adapter/session-projector.ts:437 (if (root && queueChanged(previousSnapshot.queue, next.queue))), and the reconnect seed bails out on no Turn: same file :165 (seedActive: const root = this.#snapshot.rootTurn; if (!root) return [];).
So a queue that drains while no root Turn is live is never projected, and the renderer's non-empty messageQueueBySession entry survives indefinitely — the composer keeps it renderable with entry.state === 'queued', which is what keeps 删除 enabled (packages/ui/src/composer-message-queue.tsx:286).
The failure path also deliberately leaves the projection untouched ("the projection is unchanged on failure, so there is nothing to settle here"), and the toast is the generic queue-action surface — apps/desktop/src/renderer/app-shell.tsx:1793 deleteQueuedEntry → runQueueEntryAction → showSessionError(operationFailedTitle, localizedShellErrorMessage(...)) at :1816. not_found is not a class classifyGeneralizedError recognizes (packages/core/src/redaction.ts:235 covers timeout / rate_limited / auth_failed / provider_error / network_error only), so it renders the fallback line and is reported as an unexpected-operation diagnostic.
No main-process retry is involved: the sessions:retractQueueEntry handler is a single direct call (apps/desktop/src/main/runtime-host-session-execution-ipc-main.ts:650); retryDispatchedCommand only covers one reconnect retry and only on the stop path.
Likely fix directions
- Runtime Host: drop the
root gate on queue_update (emit on queue change even with no live root Turn), and let seedActive seed the queue (even empty) when there is no root Turn, so clients can converge on reconnect.
- Desktop: treat
not_found (and session_archived) on queue.entry.retract as already-settled — remove the entry from the local projection instead of showing 操作失败, since the user's intent is already satisfied. Same for promote / update / reorder.
- Copy: route these stable codes through an explicit message instead of the generic fallback + unexpected-operation diagnostic.
Additional context
Two other error families in the same diagnostic report look like corroborating (not causal) evidence that this long-lived session had dropped events:
[2026-09-20T03:21:02Z] ERROR handler 'sessions:listActiveInteractions': Error: Runtime Host Session observer closed while opening
[2026-09-20T03:32:43Z] ERROR handler 'skills:listInvocable': RuntimeHostOperationError: Session is archived (session_archived, skill.catalog.invocable.query)
Card as rendered (screenshot, zh-CN): header 下一轮 每轮一条, body babeside的 amazon也不需要同步了, actions 编辑 / 调整方向 / 删除.
What happened
The composer keeps rendering a phantom queued-message card after the Runtime Host queue no longer holds that entry. Every attempt to delete it fails with
not_foundand a generic操作失败 / 任务操作失败,请稍后重试。toast, and the card never goes away.Observed while sending the next message in a session that had a follow-up entry queued ("下一轮 · 每轮一条"):
14 identical failures in ~4.3s (2026-09-20T04:03:14.700Z → 04:03:18.297Z, ~170ms apart), i.e. one renderer IPC call per click, with no main-process retry involved.
Expected
Either the card disappears as soon as the Host queue no longer holds the entry, or a retract of an entry the Host no longer has converges the local projection (already-settled / idempotent) instead of surfacing a generic failure.
How to reproduce
操作失败 / 任务操作失败,请稍后重试。appears and the card stays. Clicking again repeats it indefinitely.Intermittent by nature (it needs a queue change to land while the client is not observing a live Turn), but once the projection is stale it is stuck — reloading the renderer (
Cmd+R) or restarting the app clears it, switching sessions back and forth does not.Environment
b3572bc0-cf03-44fa-ab94-fde952e24a6f, healthy ("ready", 1 connection)Logs, screenshots, or additional context
Analysis (line references from
eb23ac92)The failing operation is the renderer asking the Host to delete a queue entry the Host no longer has —
packages/runtime-host/src/server/message-coordinator.ts:1766(#retractQueuedEntryAdmitted): a missingentryIdreturns exactly thisnot_found; an in-flight entry would returnoperation_conflict, an archived Sessionsession_archived, neither of which appears here.The card is stale local state, not Host state:
queue_updateevents —apps/desktop/src/renderer/app-shell-session-events.ts:306(messageQueueBySession); there is no snapshot fallback.queue_updatewhen the queue actually changed and a root Turn exists:packages/runtime-host/src/adapter/session-projector.ts:437(if (root && queueChanged(previousSnapshot.queue, next.queue))), and the reconnect seed bails out on no Turn: same file:165(seedActive:const root = this.#snapshot.rootTurn; if (!root) return [];).So a queue that drains while no root Turn is live is never projected, and the renderer's non-empty
messageQueueBySessionentry survives indefinitely — the composer keeps it renderable withentry.state === 'queued', which is what keeps 删除 enabled (packages/ui/src/composer-message-queue.tsx:286).The failure path also deliberately leaves the projection untouched ("the projection is unchanged on failure, so there is nothing to settle here"), and the toast is the generic queue-action surface —
apps/desktop/src/renderer/app-shell.tsx:1793deleteQueuedEntry→runQueueEntryAction→showSessionError(operationFailedTitle, localizedShellErrorMessage(...))at:1816.not_foundis not a classclassifyGeneralizedErrorrecognizes (packages/core/src/redaction.ts:235covers timeout / rate_limited / auth_failed / provider_error / network_error only), so it renders the fallback line and is reported as an unexpected-operation diagnostic.No main-process retry is involved: the
sessions:retractQueueEntryhandler is a single direct call (apps/desktop/src/main/runtime-host-session-execution-ipc-main.ts:650);retryDispatchedCommandonly covers one reconnect retry and only on the stop path.Likely fix directions
rootgate onqueue_update(emit on queue change even with no live root Turn), and letseedActiveseed the queue (even empty) when there is no root Turn, so clients can converge on reconnect.not_found(andsession_archived) onqueue.entry.retractas already-settled — remove the entry from the local projection instead of showing操作失败, since the user's intent is already satisfied. Same forpromote/update/reorder.Additional context
Two other error families in the same diagnostic report look like corroborating (not causal) evidence that this long-lived session had dropped events:
Card as rendered (screenshot, zh-CN): header
下一轮 每轮一条, bodybabeside的 amazon也不需要同步了, actions编辑/调整方向/删除.