openai / openai/codex

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

Open
#40,552 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

app bug session tool-calls windows-os
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

No file or test path is named. Start by locating the turn-orchestration and event-persistence entry points that emit final_answer and task_complete, then trace terminated tool outputs and dynamic developer updates near finalization. Add the suggested lifecycle integration coverage; done means one terminal event, idle task status, and normal composer input, with bounded handling for persisted final_answer events.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
backend, testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.