`wait_agent` and `close_agent` report `completed: null` when a child `TurnComplete` contains an error

Open 💬 0 comments Opened Aug 3, 2026 by tianwei-mo

What version of Codex CLI is running?

codex-cli 0.146.0

What subscription do you have?

ChatGPT-authenticated Codex environment. The exact subscription tier is not
exposed by the diagnostics used for this report.

Which model were you using?

gpt-5.6-sol, high reasoning effort.

What platform is your computer?

Linux 6.17.0-1019-aws x86_64

What terminal emulator and version are you using?

Interactive Codex TUI. The terminal emulator is not relevant to this
reproduction.

Codex doctor report

Not included in this draft. The affected parent and child rollout records are
available and contain the complete lifecycle evidence.

What issue are you seeing?

A spawned child failed its first model request. Its terminal task_complete
event correctly contained an error and no final assistant message:

{
  "type": "task_complete",
  "last_agent_message": null,
  "error": {
    "message": "Item 'msg_<redacted>' of type 'message' was provided without its required 'reasoning' item: 'rs_<redacted>'.",
    "codex_error_info": "other"
  }
}

The same child then had three contradictory externally visible statuses:

subagent notification: errored(<actual API error>)
wait_agent:             completed: null
close_agent:            previous_status.completed: null

wait_agent also returned timed_out: false, so a caller can mistake a failed
child for a successfully completed child with an empty result.

Affected child thread:

019fc932-41a0-7201-90fb-a1a82d218364

What steps can reproduce the bug?

  1. Start Codex with MultiAgent V1 enabled.
  2. Spawn a child that reaches a terminal TurnCompleteEvent with

error: Some(...) and last_agent_message: None. One deterministic way on
0.146.0 is the currently reported fork_context=true reasoning-dependency
failure from #33329.

  1. Call wait_agent for the child.
  2. Observe that the asynchronous child notification reports errored while

wait_agent reports completed: null.

  1. Call close_agent for the same child.
  2. Observe that previous_status is also completed: null.

What is the expected behavior?

  • TurnCompleteEvent { error: Some(error), ... } must derive

AgentStatus::Errored(error.message).

  • wait_agent must return the errored status and mark the wait tool call as

failed.

  • close_agent.previous_status must report the same errored status.
  • Notifications, wait results, close results, and persisted lifecycle state must

agree on the terminal outcome.

Additional information

Root cause

TurnCompleteEvent now has an optional terminal error field:

That field was added in
#32280 so unsuccessful turns can
carry terminal error details.

However, the multi-agent status reducer still maps every TurnComplete to
Completed(last_agent_message) and ignores ev.error:

EventMsg::TurnComplete(ev) =>
    Some(AgentStatus::Completed(ev.last_agent_message.clone())),

Both wait_agent and close_agent consume this derived watched status, which
explains why both return Completed(None) even though the terminal event carries
an error.

The status reducer predates TurnCompleteEvent.error. When #32280 added the
new field, its status test was updated only with error: None; no non-null error
case was added:

Suggested fix and regression coverage

Map an errored completion before the successful-completion case:

EventMsg::TurnComplete(ev) => match &ev.error {
    Some(error) => Some(AgentStatus::Errored(error.message.clone())),
    None => Some(AgentStatus::Completed(ev.last_agent_message.clone())),
},

Add tests that assert:

  1. TurnComplete(error=Some, last_agent_message=None) derives Errored.
  2. wait_agent returns that errored status and a failed collab tool status.
  3. close_agent.previous_status returns the same error.
  4. The asynchronous completion notification and tool results agree.

Duplicate search

No issue with this exact TurnCompleteEvent.error -> Completed(None) root cause
was found.

Related but distinct:

turns that silently complete without a wire-visible error. In this case the
terminal error is present; only the multi-agent status reducer discards it.

multi-agent event data in exec --experimental-json, not incorrect terminal
status derivation.

return no completed agents, not a known failed child being reported as
Completed(None).

View original on GitHub ↗