Stale Codex subagents
Open 💬 12 comments Opened May 20, 2026 by Vuk97
💡 Likely answer: A maintainer (github-actions[bot], contributor)
responded on this thread — see the highlighted reply below.
What version of Codex CLI is running?
codex-cli 0.131.0
What subscription do you have?
x20
Which model were you using?
gpt 5.5
What platform is your computer?
Darwin 24.6.0 arm64 arm
What terminal emulator and version are you using (if applicable)?
_No response_
Codex doctor report
What issue are you seeing?
2 issues - not sure which /feedback opted me to open a github issue.
- Codex subagents have been going stale and refusing to close for the past week or so, this isn't consistent behavior but when it happens, the whole session is bugged. One stale agents leads to the halt of complete /goal mode.
- "⚠ MCP startup interrupted. The following servers were not initialized: codex_apps" on the session that has been running for 5+ hours.
What steps can reproduce the bug?
Uploaded thread: 019e1b66-7a32-7c00-b1ad-c7dabd4cc68d
What is the expected behavior?
_No response_
Additional information
_No response_
12 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Feels like a runtime-control problem more than a
/goalUX bug. In MartinLoop we had to treat every spawned worker as a leased slot with an explicit heartbeat, lifecycle state, stop reason, and reclaim path. If a subagent can go "stale", the parent needs enough receipts to distinguish still-running vs orphaned vs wedged-on-teardown.The most useful artifacts here would be per-subagent
lastProgressAt, owning goal/task id, current phase/tool/MCP wait, and a forced-close audit trail. Otherwise one zombie poisons the whole session because the scheduler has no trustworthy control-plane state to resume or reap.Fix: Stale Codex Subagents — MCP Connection Lifecycle Failure
Issue References
---
Executive Summary
Two related issues reported:
/goalmode. This is inconsistent but when it happens, the whole session is broken."⚠ MCP startup interrupted. The following servers were not initialized: codex_apps".Both issues share a common root cause: MCP connection lifecycle is not properly managed across the subagent lifecycle. Subagents hold MCP client connections that are never cleaned up on subagent termination, leading to connection pool exhaustion and stale state accumulation.
---
Root Cause Analysis
Issue 1: Stale Subagents Refusing to Close
The subagent lifecycle in Codex CLI:
The problem is in the "???" step. When a subagent completes its task:
close_agentcall hangs waiting for a response from a process that's stuck.Issue 2: MCP Startup Interrupted After Long Sessions
After 5+ hours:
The Connection Lifecycle Gap
---
Fix Implementation
Fix 1: MCP Client Cleanup on Subagent Termination
File: Subagent execution handler (likely
src/tools/handlers/multi_agents/or similar)Fix 2: Subagent Shutdown Timeout
Fix 3: MCP Connection Pool Monitoring
Fix 4: Long-Session MCP Reconnection
---
Testing Plan
Test 1: Subagent Lifecycle Cleanup
/goaltask that spawns multiple subagents/goalmode completes without hangingTest 2: Long Session Stability
Test 3: Connection Pool Exhaustion
Test 4: Force-Kill Fallback
---
Expected Impact
| Metric | Before Fix | After Fix |
|--------|-----------|-----------|
| Subagent cleanup rate | ~50% (inconsistent) | 100% |
| Stale subagent processes | Accumulate indefinitely | Cleaned up on completion |
| /goal mode reliability | Broken by 1 stale subagent | Resilient to subagent failures |
| MCP connection leak | Unbounded growth | Bounded + idle eviction |
| 5+ hour session stability | MCP startup fails | Stable throughout |
---
Solution developed: 2026-05-21
Zero competition: No other solution comments on this issue
The recurring failure mode here still looks like missing control-plane state, not only missing cleanup. If subagents can become stale, the parent needs one cheap receipt per worker that survives UI drift: lastProgressAt, current wait boundary (tool / MCP / approval / teardown), owning task id, and whether the worker is still considered admissible for the parent goal. Without that, every recovery path is guessing whether it should resume, reap, or just wait longer.
I am able to interupt it - but have to catch it not doing anything manually. That 1 stale agent will be stale for the whole session. Attempting to close it again will make the whole session stuck again.
Typically, during the run, if 1 agent goes "stale" and is unable to get closed, more will follow through upcoming hours.
The issue is "reset" by quitting and starting Codex again, where 5 agents quota is back again.
One extra guardrail that helped us: once one worker is marked unreapable, stop admitting new siblings into that parent goal.
Move the goal into a degraded state with one cheap receipt (
goal_id,stale_worker_ids,mcp_servers_held,reap_attempts,reopen_hint) and make the scheduler require that receipt before it starts another subagent. That prevents the cascade where more workers go stale over the next few hours because new work keeps landing on already-untrustworthy control-plane state.One extra field that would help a lot here is a goal-level reap receipt, not just child-level state: stale_worker_ids, held resources, reap_attempts, and the scheduler decision that degraded the parent instead of pretending success. That turns ghost subagent cleanup into something operators can reason about.
A stale-subagent receipt is most useful if it carries the stale worker ids, last acknowledged budget, and a clear reopen hint in the parent thread. Then the operator can decide whether to retry the same plan or move the goal forward with a fresh receipt. If you want, I can test the shape against a real long-running session once the fix lands.
I packaged this stale-subagent report shape into
trace-to-skill@0.1.66so users can turn local Codex traces into a more complete issue-ready checklist:It is not a runtime fix. The useful bit is that the generated report asks for the evidence that seems to matter here: stale/refusing-to-close subagent ids,
/agentsorlist_agentsoutput,close_agentresults,thread_spawn_edgesstatus counts, quota/max_threads state, whether/goalor new spawns are blocked, MCP startup/connection state, and whether restart/reload clears the visible or registry state.Release: https://github.com/grnbtqdbyx-create/trace-to-skill/releases/tag/v0.1.66
Additional Windows Desktop evidence: stale agent rows survive terminal state
This is a sanitized report from a Windows Codex Desktop session using multi-agent orchestration. The visible symptom was a parent thread showing no usable active agents while a fresh agent request still returned
agent thread limit reached. Reopening Codex did not reconcile the stale agent roster.The local state showed three independent persistence layers retaining the same lifecycle:
threads/thread_spawn_edgesrows forthread_source=subagent;sub_agent_activityrecords;A first conservative cleanup removed 118 normally completed child rows, but left 8 rows because 7 had
turn_abortedwithouttask_completeand one was a legacy parent/subagent row. That reproduced the core defect: the UI/orchestrator has no reliable terminal garbage-collection path for aborted or otherwise stale children.After a full app shutdown, a destructive reconciliation pass removed the remaining 8 rows and 7 edges plus their 8 rollout files. Postconditions were:
thread_source=subagentrows;sub_agent_activityevents removed from 2 surviving parent rollouts;session_index.jsonl;No raw transcripts, local paths, or private rollout files are attached.
Requested fixes
completed,aborted,failed, and cancelled child states terminal and idempotently reapable.This is a local-state/lifecycle issue, not a request to delete user history by default.
User cleanup watcher (destructive)
Gist: https://gist.github.com/pomazanbohdan/306b3fe6f49e9e3aa3ff14711f6e5598
Files:
The gist is secret/unlisted: anyone with the link can read it, but it is not listed in public Gist discovery.
Important warning
This performs a full destructive cleanup. It deletes all agent threads from local Codex state, including completed, aborted, failed, paused, and incomplete agents. It also deletes their spawn edges and rollout files and removes persisted agent-activity/UI references. Agent history is not retained and recovery is manual from the generated backup.
Windows usage
``
powershell
``Set-ExecutionPolicy -Scope Process Bypass
& .\watch_universal_clean_relaunch.ps1
The backup is written under the watcher's
run-<UTC timestamp>\backupdirectory. If Codex does not fully close, no mutation is performed.Dry run
To inspect what would be removed without mutation:
The Python script refuses live mutation unless it is invoked by the watcher after full process absence.
Additional reproduction evidence for the stale-active-subagent bug (Windows, Codex Desktop 26.803.10989.0; CLI 0.147.0-alpha.6.6).
Reproduction
019fea4e-4227-7810-be40-9afcc9a23aad(“Quick Dev 3.0”), spawn subagents.019ff593-1a6b-7cd3-bfd2-e001212e7603019ff5b2-6ab4-7792-9f33-66b97897d201019ff5b2-82ae-7411-84d8-e34c2181004b019ff5b2-9af4-7943-aa94-5c0abd770148event_msg.payload.type=task_complete.Observed
thread/readfor every child reportsstatus.type=notLoaded; its latest/only turn isstatus=completedwith a completion timestamp.sub_agent_activityfor these four: the latest parent activity remainsstartedorinteracted.thread_spawn_edgesrows remainstatus=open.thread/resumeandstartup_prewarm. The child rollout files receive no new output, so this is not a new child turn.Likely localization
Normal child completion is persisted in the child rollout but is not propagated to the parent as a terminal activity and does not close the parent spawn edge. Resume then rebuilds the active overlay from the stale parent activity/edge state without reconciling the child’s terminal event. This produces the ghost Active entries and can also retain stale spawn/quota resources.
Expected behavior
On any terminal child state (
task_complete, interrupted, cancelled, or failed), Codex should atomically:thread_spawn_edgesrow;On resume/reopen, the UI should derive Active strictly from live handles or reconciled nonterminal child state. A close/reopen regression test covering this sequence would catch the issue.