Subagents can shut down without delivering results; fork_turns none scouts acted as orchestrators
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.
- In a parent Codex Desktop thread, first attempt to spawn multiple scouts with
agent_typeplus a full-history fork. - 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.
- 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"
- Attempt a fourth scout. It fails with:
collab spawn failed: agent thread limit reached
- Parent waits repeatedly:
wait_agent({"timeout_ms":30000})-> timed outwait_agent({"timeout_ms":60000})-> timed outwait_agent({"timeout_ms":90000})-> timed outwait_agent({"timeout_ms":180000})-> timed out
- Parent checks agents:
- Early
list_agentsshowed the three scouts asrunning. - Later
list_agentsshowed the three scouts asshutdown. - No final answer/mailbox payload had arrived in the parent.
- Parent tries to close them and gets:
collab tool failed: thread 019e9eb0-2d48-7dc3-93c8-ba9241c99566 not foundcollab tool failed: thread 019e9eb0-2ef6-72e3-a2b4-34eb785fdfd8 not foundcollab 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 returnedagent thread limit reached. - Lines 262-263:
wait_agent30s timeout. - Lines 286-287:
wait_agent60s timeout. - Lines 292-293:
wait_agent90s timeout. - Lines 298-299:
wait_agent180s timeout. - Lines 268-269:
list_agentsshowed the three scouts asrunning. - Lines 304-309:
close_agentreturnedthread <id> not foundfor all three child ids. - Lines 314-315:
list_agentsshowed the three scouts asshutdown, 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_agenton 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 wasaborted 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_agenton 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, andclose_agentshould agree on lifecycle state or return a structured explanation of the mismatch.thread <id> not foundshould not be the only close/readback signal for a thread that was previously spawned and visible throughlist_agents.- Thread capacity should be discoverable before spawn, or
spawn_agentshould return enough status onagent thread limit reachedto recover cleanly.
Actual behavior
- The parent received no usable scout content and no child final-answer payload.
wait_agentonly returned timeouts, even though child turns later ended/interrupted.list_agentstransitioned fromrunningtoshutdown, but the parent still received no terminal child result.close_agentreturnedthread <id> not foundfor 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_agentdid 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 foundafter 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 reachedand 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_agentable to surface terminal status updates for shutdown/interrupted children, not only final answer payloads. - Reconcile
list_agentsandclose_agentstates. If a listed/shutdown child has no live thread, return a structuredalready_shutdown/not_found_but_spawn_was_recordedsignal instead of a genericthread not foundfailure. - Expose capacity/status preflight before spawning, or include active capacity and live child list in the
agent thread limit reachedresponse. - Add regression coverage for:
fork_turns: "none"child with a narrow scout prompt must not callwait_agent/close_agentunless the prompt asks it to.- child interrupted after startup must generate a parent-visible terminal event.
- parent
wait_agentmust observe child terminal events. list_agents/close_agentlifecycle consistency after child shutdown or interrupted turn.
3 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Additional reproduction on Codex CLI
0.144.3in the Desktop/MultiAgent runtime:separate clean Git worktrees. Each prompt was narrow and did not request
orchestration or sibling coordination.
runningfor several minutes without returning aterminal payload, handoff, test result, or filesystem edit. The parent only
received repeated
wait_agenttimeouts.clean.
list_agentscontinued to show the children asinterrupted.spawn_agentfailed with `collab spawn failed: agent threadlimit 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-onlyprobe 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.
Additional source-level evidence for this issue:
Current
mainat0fb559f0appears to contain a concrete gap in the MultiAgentV2 lifecycle:wait_agentsubscribes only to the parentInputQueueActivity; it does not observe child lifecycle state.TurnCompleteorTurnAborted.InternalAgentDiedcleanup removes the child without notifying the parent mailbox.As a result, if a V2 child disappears without emitting
TurnCompleteorTurnAborted, no event reaches the parent input queue andwait_agentcannot observe the child’s death.More fundamentally, the V2
wait_agentAPI has no child identifier or target list: its only argument istimeout_ms. This prevents the handler from validating the child before blocking or returningNotFoundfor 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
NotFoundstate before subscribing.