[Windows] final_answer persists without task_complete, composer remains in Insert mode
Codex Desktop turn remains active after final_answer, so the composer only offers Insert
Summary
In a long-running Codex Desktop task, the assistant's final answer is rendered, but the turn sometimes remains active. The composer therefore continues to show the Insert/steer behavior instead of allowing a normal next message.
This is confusing because the visible assistant response has already ended and may itself contain a small follow-up question, but the user cannot answer it as a new turn.
Environment
- Codex Desktop package:
OpenAI.Codex_26.818.8289.0_x64 - OS: Windows
10.0.26200.0 - PowerShell:
7.6.4 - Task type: local desktop task backed by a Git repository
- Task history: very long; multiple earlier compactions/continuations
- No app terminal session was attached to the task at diagnosis time
Observed behavior
- The assistant performs several tool calls.
- Some network-backed commands yield without completing and are later explicitly terminated.
- The assistant emits a visible
final_answercontaining a short clarification question. - The UI still shows the task as working.
- The composer only permits Insert/steer instead of starting a normal user turn.
- The task eventually has to be interrupted/aborted before normal interaction resumes.
Expected behavior
Once a final_answer has been persisted and all tool calls have terminal results, the turn should emit task_complete, transition the task to idle, and restore the normal send behavior.
If the backend is still doing required post-processing, the UI should distinguish that state from model/tool execution and should not leave a visible clarification question that the user cannot answer normally.
Sanitized event evidence
The local JSONL event stream showed the following ordering (timestamps are local log timestamps; message contents and tool arguments are omitted):
04:52:02 custom_tool_call exec -> completed
04:52:53 function_call wait
04:53:14 function_call_output
04:53:22 function_call wait
04:53:43 function_call_output
04:53:48 function_call wait (terminate)
04:53:48 function_call_output
04:53:57 custom_tool_call exec -> completed
04:54:36 function_call wait (terminate)
04:54:37 function_call_output
04:54:47 custom_tool_call exec -> completed
04:55:25 function_call wait (terminate)
04:55:25 function_call_output
04:55:45 agent_message phase=final_answer
04:55:45 response_item message role=assistant phase=final_answer
04:55:45 token_count
[no task_complete event]
04:57:33 response_item message role=developer
04:59:03 turn_aborted
The important invariant violation is final_answer without a subsequent task_complete, even though every visible tool call had a terminal output and no curl or gh child process remained.
Frequency
Intermittent, but repeatedly noticed in this specific long task. Shorter tasks reportedly do not exhibit the same behavior as often.
Ranked hypotheses
- A terminated yielded tool cell produces a terminal tool output but leaves the turn orchestration state waiting for another completion signal.
- A late dynamic developer/context refresh races with final-answer completion and prevents
task_completefrom being emitted. - Long-thread compaction or persistence delays expose the race more frequently.
- Less likely: the backend turn is already complete but the composer fails to refresh. The captured event stream argues against this because
task_completeitself is absent.
Suggested regression coverage
Add an integration test for this lifecycle:
start turn
-> tool call yields a running cell
-> terminate/wait returns terminal output
-> assistant emits final_answer
-> dynamic context/developer update arrives near finalization
-> assert exactly one terminal turn event
-> assert task status becomes idle
-> assert composer accepts a normal user message
Also assert that every persisted final_answer is followed by either task_complete or an explicit terminal error/abort within a bounded time.
Workaround
- Avoid launching additional network probes after enough evidence is available.
- Explicitly terminate every yielded tool cell and confirm no child process remains.
- If the final answer is visible but the composer still shows Insert, stop the current turn or open a fresh/forked task.
- For clarification-only exchanges, avoid tool calls so the turn has fewer lifecycle edges.
Privacy
This report intentionally excludes user prompts, project paths, API keys, tool arguments, model reasoning, and task identifiers. The full local event trace can be supplied privately if requested.
2 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Thanks. I reviewed #39988 and #39572. This report appears related at the turn-lifecycle layer, but the observed failure point is different.
In both suggested issues, the turn stalls before producing a final answer. In #40552, the
final_answerwas already rendered and persisted, and every visible tool call had a terminal output, but notask_completefollowed. The composer therefore remained in Insert/steer mode until the turn was aborted.The captured ordering was:
No residual
curl.exeorgh.exechild process remained when this was diagnosed. This may therefore be a finalization-state race after a persisted final answer, rather than a model-stream stall before completion.