Subagents can shut down without delivering results; fork_turns none scouts acted as orchestrators

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

Summary

In Codex Desktop / MultiAgentV2, a parent thread spawned three read-only scout subagents with fork_turns: "none". The parent never received a final answer or mailbox result from any child. Repeated wait_agent calls timed out on schedule; list_agents later showed the children as shutdown; close_agent then returned thread <id> not found.

Local child rollout logs show the children were not merely idle. Despite self-contained read-only scout prompts, they appear to have acted like orchestration agents: they called wait_agent, attempted close_agent on sibling scouts, emitted commentary about other scouts not returning, and then their turns were interrupted/aborted. None delivered the requested scout findings to the parent.

This makes subagents unreliable for long planning/review/scout workflows: the parent cannot distinguish "still running," "shutdown without result," "lost mailbox result," "thread not found," or "child misinterpreted inherited orchestration context."

Environment

  • Variant: Codex Desktop / app-side multi-agent tools
  • CLI visible locally: codex-cli 0.137.0
  • Platform: macOS 26.6, Darwin 25.6.0 arm64
  • Account/subscription: ChatGPT Pro
  • Parent rollout/session:
  • /Users/jstar/.codex/sessions/2026/06/06/rollout-2026-06-06T13-43-41-019e9ead-5b80-7e40-8f53-ed9e915b86a4.jsonl
  • session id 019e9ead-5b80-7e40-8f53-ed9e915b86a4
  • Child rollout/session ids:
  • runtime/jobs scout: 019e9eb0-2d48-7dc3-93c8-ba9241c99566
  • frontend workflows scout: 019e9eb0-2ef6-72e3-a2b4-34eb785fdfd8
  • docs/contracts scout: 019e9eb0-30d3-7bb3-97d4-6cd2a34845ac

Reproduction shape

This was observed during a repo planning task where the parent tried to fan out read-only exploration scouts.

  1. In a parent Codex Desktop thread, first attempt to spawn multiple scouts with agent_type plus a full-history fork.
  2. Observe spawn validation error:
  • Full-history forked agents inherit the parent agent type, model, and reasoning effort; omit agent_type, model, and reasoning_effort, or spawn without a full-history fork.
  1. Retry with self-contained prompts and fork_turns: "none":
  • scout_runtime_jobs, agent_type: "explorer"
  • scout_frontend_workflows, agent_type: "explorer"
  • scout_docs_contracts, agent_type: "explorer"
  1. Attempt a fourth scout. It fails with:
  • collab spawn failed: agent thread limit reached
  1. Parent waits repeatedly:
  • wait_agent({"timeout_ms":30000}) -> timed out
  • wait_agent({"timeout_ms":60000}) -> timed out
  • wait_agent({"timeout_ms":90000}) -> timed out
  • wait_agent({"timeout_ms":180000}) -> timed out
  1. Parent checks agents:
  • Early list_agents showed the three scouts as running.
  • Later list_agents showed the three scouts as shutdown.
  • No final answer/mailbox payload had arrived in the parent.
  1. Parent tries to close them and gets:
  • collab tool failed: thread 019e9eb0-2d48-7dc3-93c8-ba9241c99566 not found
  • collab tool failed: thread 019e9eb0-2ef6-72e3-a2b4-34eb785fdfd8 not found
  • collab tool failed: thread 019e9eb0-30d3-7bb3-97d4-6cd2a34845ac not found

Evidence from parent rollout

From parent rollout /Users/jstar/.codex/sessions/2026/06/06/rollout-2026-06-06T13-43-41-019e9ead-5b80-7e40-8f53-ed9e915b86a4.jsonl:

  • Lines 26-29: first spawn attempts with agent_type + fork_turns: "all"; all returned the full-history fork validation error.
  • Lines 38-45: retry with fork_turns: "none"; three scouts spawned successfully and the fourth returned agent thread limit reached.
  • Lines 262-263: wait_agent 30s timeout.
  • Lines 286-287: wait_agent 60s timeout.
  • Lines 292-293: wait_agent 90s timeout.
  • Lines 298-299: wait_agent 180s timeout.
  • Lines 268-269: list_agents showed the three scouts as running.
  • Lines 304-309: close_agent returned thread <id> not found for all three child ids.
  • Lines 314-315: list_agents showed the three scouts as shutdown, still without any parent-delivered result payload.

Evidence from child rollouts

The child rollouts suggest this was not just slow/empty execution. The children appear to have drifted into orchestrator behavior and then aborted/interrupted.

  • Docs/contracts scout, child session 019e9eb0-30d3-7bb3-97d4-6cd2a34845ac:
  • called wait_agent({"timeout_ms":30000}), timed out
  • emitted commentary about scout threads still running
  • attempted close_agent on sibling scouts
  • close outputs reported aborted by user after 0.1s
  • turn ended with reason: "interrupted", duration about 279s
  • Frontend workflows scout, child session 019e9eb0-2ef6-72e3-a2b4-34eb785fdfd8:
  • called wait_agent({"timeout_ms":60000}), timed out
  • emitted commentary that scout agents did not return
  • attempted to close sibling/current scouts
  • one close output reported thread ... not found; another was aborted by user after 0.1s
  • turn ended with reason: "interrupted", duration about 326s
  • Runtime/jobs scout, child session 019e9eb0-2d48-7dc3-93c8-ba9241c99566:
  • called wait_agent({"timeout_ms":10000}), timed out
  • emitted commentary that the frontend/doc scouts had shut down
  • attempted close_agent on itself
  • close output reported aborted by user after 0.1s
  • turn ended with reason: "interrupted", duration about 375s

Expected behavior

  • A child spawned with fork_turns: "none" and a self-contained read-only prompt should treat that prompt as the active task and should not start coordinating sibling agents unless explicitly instructed.
  • If a child shuts down, is interrupted, or aborts before producing a normal final answer, the parent should receive a structured terminal event through wait_agent/mailbox.
  • wait_agent, list_agents, and close_agent should agree on lifecycle state or return a structured explanation of the mismatch.
  • thread <id> not found should not be the only close/readback signal for a thread that was previously spawned and visible through list_agents.
  • Thread capacity should be discoverable before spawn, or spawn_agent should return enough status on agent thread limit reached to recover cleanly.

Actual behavior

  • The parent received no usable scout content and no child final-answer payload.
  • wait_agent only returned timeouts, even though child turns later ended/interrupted.
  • list_agents transitioned from running to shutdown, but the parent still received no terminal child result.
  • close_agent returned thread <id> not found for each child.
  • Child rollouts show children calling orchestration tools (wait_agent, close_agent) instead of simply completing their assigned scout prompts.

Related issues checked

I did not find an exact duplicate for this combined failure mode: child shutdown/no parent-delivered final result + not found cleanup/readback + fork_turns: "none" scouts acting as orchestrators.

Adjacent issues:

  • #26728 - Subagent completion result can be lost after parent thread compaction
  • Closest on lost child completion, but in this case no explicit parent compaction was observed during the wait cycle, and the child logs show interruption/orchestration drift rather than a normal completed child result being hidden after compaction.
  • #24951 - multi_agent wait_agent/spawn_agent can block for ~7.5h; wait_agent timeout_ms not enforced during runtime stall
  • Related to wait/spawn control-plane reliability. Here, wait_agent did return on timeout; the problem is missing terminal/result delivery and ambiguous lifecycle state.
  • #25426 - close_agent can hang forever after marking a subagent closed if the child thread never terminates
  • Related lifecycle/close problem. Here, close did not hang; it returned thread <id> not found after the parent had seen the children as spawned/listed.
  • #23479 - Main agent needs subagent capacity/status preflight before spawn_agent attempts
  • Related to the fourth scout failing with agent thread limit reached and no clear preflight/capacity status.
  • #23930 - Codex app subagent cards can remain stuck/visible after close while close/readback reports no live agent
  • Related readback mismatch. This report is specifically about tool-level parent mailbox/result delivery and lifecycle status.
  • #26130 - Parallel spawn_agent children can receive sibling prompt envelopes with fork_turns none
  • Related isolation issue. This report does not prove sibling prompt-envelope leakage, but it does show children behaving as sibling orchestrators despite fork_turns: "none" and narrow scout prompts.

Impact

This makes subagents brittle for exactly the workflows where they are most valuable: parallel repo scouting, review/audit variance reduction, long-running research branches, and delegated planning. The parent thread cannot rely on subagent output unless a final payload actually arrives, and the failure mode consumes time/context while leaving no trustworthy result.

For user-facing Codex Desktop behavior, this feels like subagents silently disappearing or becoming confused about their role. It also makes it hard to file precise bug reports because parent-visible state and child-local state disagree.

Suggested fix direction

  • Strengthen the active task boundary for spawned children, especially fork_turns: "none" children with role-scoped prompts. Parent/team orchestration instructions should not cause a child to manage sibling agents unless that is explicitly delegated.
  • Always deliver a child terminal state to the parent mailbox, even if there is no final answer: completed, interrupted, aborted, shutdown_without_result, not_found_after_spawn, etc., with child thread id and last known status.
  • Make wait_agent able to surface terminal status updates for shutdown/interrupted children, not only final answer payloads.
  • Reconcile list_agents and close_agent states. If a listed/shutdown child has no live thread, return a structured already_shutdown / not_found_but_spawn_was_recorded signal instead of a generic thread not found failure.
  • Expose capacity/status preflight before spawning, or include active capacity and live child list in the agent thread limit reached response.
  • Add regression coverage for:
  • fork_turns: "none" child with a narrow scout prompt must not call wait_agent/close_agent unless the prompt asks it to.
  • child interrupted after startup must generate a parent-visible terminal event.
  • parent wait_agent must observe child terminal events.
  • list_agents/close_agent lifecycle consistency after child shutdown or interrupted turn.

View original on GitHub ↗

3 Comments

github-actions[bot] contributor · 2 months ago

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

  • #25458
  • #26130

Powered by Codex Action

aliwatters · 1 month ago

Additional reproduction on Codex CLI 0.144.3 in the Desktop/MultiAgent runtime:

  1. A parent agent dispatched several self-contained implementation workers into

separate clean Git worktrees. Each prompt was narrow and did not request
orchestration or sibling coordination.

  1. Three workers remained running for several minutes without returning a

terminal payload, handoff, test result, or filesystem edit. The parent only
received repeated wait_agent timeouts.

  1. The parent interrupted the workers after verifying their worktrees were

clean. list_agents continued to show the children as interrupted.

  1. A subsequent spawn_agent failed with `collab spawn failed: agent thread

limit reached`, leaving no way to launch a fresh recovery worker in the same
parent session.

This matches the combined failure here: children can fail to deliver a useful
terminal result, and retained interrupted/completed child threads exhaust the
spawn quota. The parent cannot recover by starting a fresh worker even when no
child has touched the worktree.

Control: a minimal standalone codex exec --ephemeral --sandbox read-only
probe completed successfully in the same environment, so this observation is
about the MultiAgent lifecycle/capacity path rather than general model
connectivity.

Expected recovery behavior: every interrupted or shutdown child should emit a
parent-visible terminal event and release its spawn slot; alternatively the
parent needs a supported cleanup/retry path with capacity diagnostics.

0xdeafbeef · 1 month ago

Additional source-level evidence for this issue:

Current main at 0fb559f0 appears to contain a concrete gap in the MultiAgentV2 lifecycle:

As a result, if a V2 child disappears without emitting TurnComplete or TurnAborted, no event reaches the parent input queue and wait_agent cannot observe the child’s death.

More fundamentally, the V2 wait_agent API has no child identifier or target list: its only argument is timeout_ms. This prevents the handler from validating the child before blocking or returning NotFound for a removed thread.

At minimum, a targetless wait should return immediately when the parent has no live descendants and no pending mailbox activity. Preferably, the API should accept explicit child targets and return their current terminal or NotFound state before subscribing.