[Windows] final_answer persists without task_complete, composer remains in Insert mode

Open 💬 2 comments Opened Aug 25, 2026 by tao943
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

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

  1. The assistant performs several tool calls.
  2. Some network-backed commands yield without completing and are later explicitly terminated.
  3. The assistant emits a visible final_answer containing a short clarification question.
  4. The UI still shows the task as working.
  5. The composer only permits Insert/steer instead of starting a normal user turn.
  6. 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

  1. A terminated yielded tool cell produces a terminal tool output but leaves the turn orchestration state waiting for another completion signal.
  2. A late dynamic developer/context refresh races with final-answer completion and prevents task_complete from being emitted.
  3. Long-thread compaction or persistence delays expose the race more frequently.
  4. Less likely: the backend turn is already complete but the composer fails to refresh. The captured event stream argues against this because task_complete itself 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.

View original on GitHub ↗

2 Comments

github-actions[bot] contributor · 3 days ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #39988
  • #39572

Powered by Codex Action

tao943 · 3 days ago

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_answer was already rendered and persisted, and every visible tool call had a terminal output, but no task_complete followed. The composer therefore remained in Insert/steer mode until the turn was aborted.

The captured ordering was:

tool calls -> terminal outputs
-> agent_message phase=final_answer
-> response_item phase=final_answer
-> token_count
-> [no task_complete]
-> later developer/context event
-> turn_aborted

No residual curl.exe or gh.exe child 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.