VS Code queued follow-ups stop processing after completed turn resumes as markedStreaming=true
What version of the IDE extension are you using?
26.601.21317
What subscription do you have?
Pro
Which IDE are you using?
VS Code
What platform is your computer?
Linux 6.8.0-117-generic x86_64 x86_64
What issue are you seeing?
The VS Code Codex extension task queue can stop processing queued follow-ups after a completed turn is restored as streaming.
I had multiple queued follow-ups in one thread. One task completed, but later queued tasks did not start. The queued follow-ups were still persisted, so the queue had not actually emptied.
Relevant log pattern:
maybe_resume_success conversationId=<redacted> latestTurnStatus=completed markedStreaming=true
I also saw related queue/steering symptoms:
[queued-followups] Failed to release send lock conversationId=<redacted> error={}
Received app server error error={"code":-32600,"message":"no active turn to steer"}
This appears related to #19690, but this report is specifically about the queued-follow-up processor stopping with messages still persisted.
What steps can reproduce the bug?
- Open a thread in the VS Code Codex extension.
- Submit a task.
- While the task is running, queue multiple follow-up messages in the same thread.
- Let one queued task complete.
- Reload/restart VS Code or otherwise trigger thread resume.
- Check the Codex log.
- In the failing case, the latest turn is completed but resume logs
markedStreaming=true. - The queued follow-up list still contains messages, but the queue stops processing them.
No code snippet is involved; this appears to be extension/session queue state.
What is the expected behavior?
A completed turn should not be restored as actively streaming.
If queued follow-ups remain and the latest turn is completed, the extension should start the next queued follow-up instead of leaving the queue stopped.
The queue should not require an already-active stream in order to start the next queued turn after a completed turn.
Additional information
Possible fix direction from local debugging:
- Only mark a resumed/snapshotted conversation as streaming if the latest turn is
inProgress. - Clear streaming state on terminal
turn/completed. - Do not restore queued follow-ups with
pausedReasonafter a normal completed turn. - Ensure queued follow-up send-lock release succeeds.
- Treat stale
no active turn to steer/active_turn_not_steerableas inactive-turn queue state. - Allow starting the next queued follow-up after a completed turn.
3 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Follow-up after more reproductions:
The queue stopped again across multiple later reproductions after the stale-streaming fix direction. In these later cases, the logs no longer pointed to the earlier completed-turn-restored-as-streaming issue.
Observed pattern:
completed.markedStreaming=false.no active turn to steer.queued-follow-up-send-lock-acquire.This points to another queue-side readiness blocker.
A likely remaining issue is that the queued-follow-up readiness predicate appears to require the latest completed turn to contain a renderer item typed exactly
agentMessageor a manualcontextCompactionbefore draining queued follow-ups. That seems too strict. A terminalcompletedturn should be enough to start the next queued follow-up when:inProgress.If a completed turn has a different renderer item shape, the queue can silently retry forever without ever attempting the send lock.
Suggested additional fix:
Do not require
latestTurn.items.some(item => item.type === "agentMessage")before draining queued follow-ups. For queued follow-ups, a terminalcompletedlatest turn should allow the next queued message to start. Retry should be reserved for genuinely transient not-ready states, such as an activeinProgressturn or missing conversation state.I confirmed another queued-follow-up failure mode that appears tied to the VS Code webview/renderer lifecycle.
In one reproduction, a queued task completed while VS Code was not the foreground application:
2026-06-04T17:16:04.833ZUTC (2026-06-05 02:16:04 JST)2026-06-04T17:25:14.578ZUTC (2026-06-05 02:25:14 JST)549.7s1.1sThis suggests the queued-follow-up drain path is still dependent on webview/renderer focus or lifecycle. Local bundle inspection showed the queue worker was running in the webview and originally consumed turn completion through a renderer atom/effect path. I also found a stale-state edge where the queue guard could see
latestTurn.status === "inProgress"afterturn/completedhad already cleared the stream role, causing it to rely on a delayed retry timer.The local workaround I tested was:
latestTurn.status === "inProgress"if the stream role still says the client owns an active streamaddTurnCompletedListenerinstead of draining through the turn-completed atom watcherAfter applying those local changes, the next observed handoffs in the same queue returned to immediate timing, e.g.:
2026-06-04T17:36:40.526Zcomplete ->2026-06-04T17:36:40.666Znext start (0.1s)1.1sI do not think a webview-only fix is fully robust. The durable upstream fix should make queued-follow-up draining, or at least the wake/retry scheduling after
turn/completed, run from the extension host/app-server side rather than depending on a focused/active webview renderer.