Codex app accumulates stale subagents in cache/UI and they cannot be closed reliably
What version of the Codex App are you using (From “About Codex” dialog)?
Versión 26.527.31326 (3390)
What subscription do you have?
Plus Plan
What platform is your computer?
Darwin 25.5.0 arm64 arm
What issue are you seeing?
Summary
In a long-running Codex desktop session that uses many subagents, the app appears to accumulate stale subagent entries in the UI/cache. Completed or already-closed agents can remain visible, and closing them is not always reconciled cleanly with backend state. Over time, this makes it difficult to tell which subagents are actually still alive.
Actual behavior
After repeated spawn/complete/close cycles:
- many old subagents remain visible long after completion
- some close attempts return
shutdown - some close attempts return
not found - some agents appear completed but still remain listed
- the visible subagent count can grow very large (in my case, 100+)
Why this seems like an app bug
This does not appear to be tied to a specific repo workflow. In my case, the stale subagent buildup was separate from the actual project state, and some close attempts returning not found suggest the app UI/cache and backend agent state can drift out of sync.
Suggested debugging angles
- UI cache vs backend state reconciliation for agent lifecycle
- pruning/GC of completed subagents
- handling of
close_agentfor already-completed or already-evicted agents - stale thread/agent handles remaining visible after shutdown
- whether this only affects the UI list or also impacts future spawning
Screenshot
<img width="309" height="299" alt="Image" src="https://github.com/user-attachments/assets/e23f2b1a-623e-41cb-9fed-922eaaf8b995" />
What steps can reproduce the bug?
- Start a long Codex desktop session in a repo and use subagents heavily over time.
- Spawn many subagents across multiple roles (for example: researcher, planner, reviewer, coder, trainer).
- Let many of them finish normally.
- Close completed subagents from the agent controls and/or via tool-driven close requests.
- Continue spawning and closing more subagents in the same long-running session.
- Revisit the subagent list/sidebar after enough cycles.
Observed
- many old subagents are still shown
- some can no longer be closed cleanly
- some close attempts return
not found - some already-completed agents remain visible as if still retained in cache/UI
- the visible count can grow very large (in my case, 100+)
What is the expected behavior?
Completed or explicitly closed subagents should no longer remain in the active visible subagent list.
The app should reconcile UI state with backend state reliably:
- if an agent is completed, it should appear as completed and be removable/archived cleanly
- if an agent is closed, it should disappear from the live list or move to a clearly separate historical section
- if an agent no longer exists, the UI should not keep showing it as lingering cached state
Repeated spawn/complete/close cycles in a long session should not cause the visible subagent count to grow indefinitely with stale entries.
Additional information
_No response_
23 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Additional diagnostic evidence from another affected Codex Desktop macOS install.
Environment:
Observed behavior:
agent ... not found, suggesting the live agent registry no longer had those handles.No AppServerManager registered for conversationId: ....Local state observations, sanitized:
state_5.sqlite.thread_spawn_edgesinitially had 184 rows for the affected parent thread; all werestatus = 'closed'.threadsrows were still visible enough to keep contributing to UI clutter until manually marked archived.session_meta.source.subagent.thread_spawn.parent_thread_idfound 386 subagent rollout files associated with the same parent thread:state_5.sqlite.threadsthread_spawn_edges.Why this matters:
thread_spawn_edges,threads.archived, rollout JSONLs, and renderer cache.Expected behavior:
threadsshould not be reattached to the parent UI as active/current subagents.I avoided including local paths, project names, thread IDs, or transcript contents here. I can provide more specific diagnostics if there is a private/debug-safe channel.
Adding another datapoint from Codex Desktop/macOS with an uploaded feedback thread.
Uploaded feedback thread:
019ee572-2862-7903-8fa5-15d4c747fbccEnvironment:
0.142.0-alpha.60.141.0gpt-5.5Darwin 25.0.0 arm64 arm, macOS26.0.1Observed behavior:
Local evidence:
019edeb1-707c-7fc2-9ad4-81d1aed871f9Build d3 iOS apptask_complete, but the UI still surfaced them under Subagents:019ee5a9-5b49-7ce1-91ef-51ec1d127a7f019ee5a9-5cad-7fc2-9fb7-97aa9c6b619d019ee5a9-5e6b-77c0-8e43-ca9be5492fa9019ee5a9-60af-7590-911f-fdb3781d36bc019ee5a9-6344-7031-8c23-789a0c61700e019ee5a9-be0d-70b1-81b0-f2d73cd8f34eExpected:
Note:
codex doctor --jsonwas run from a sandboxed Codex session and included expected network reachability failures from the sandbox. The local state inventory itself reported rollout DB parity as OK.Seen same issue. Feedback: 019f04ff-b2ce-7302-be1a-b511e0ee7cc1
Compaction purges the subagents IDs from the context, and there's no tool that can be used to get those IDs back, so Codex has no way of closing the subagents.
In a long orchestration agent session, eventually the subagent limit is reached due to the leak and then all further subagents fail to be created.
Additional reproduction from a long-running Linux Codex session (Codex CLI 0.145.0):
multi_agent_v1__*lifecycle tools.Impact: capacity accounting appears to include stale completed/interrupted children, so a review-fix-review workflow can dead-end late in a task. Expected behavior: completed or interrupted children should stop consuming spawn capacity, or callers should have a reliable close/prune control and an accurate capacity/status API.
Separately, I attempted the documented isolated
codex execreviewer fallback. It launched with the requested model/effort and read-only sandbox, but repeatedly emitted a large tool transcript without a usable final review verdict, so it could not substitute for the final review gate. This may be a related fallback/reporting issue.Windows corroboration from a long-running
/goalsession on July 23, 2026.A single Codex Desktop screen showed:
The screenshot cannot prove whether the five
workingchildren were truly live or stale UI/cache entries. That is precisely the lifecycle ambiguity described here: after the parent is paused and unable to continue, the app still presents children as working without exposing authoritative backend handles, pause-propagation results, or a stale/historical classification.A separate state-reconciliation report was filed as #35052 because the same screen also shows the parent pause, usage gate, step counter, and subagent counts disagreeing. This comment is the narrower cross-platform corroboration for stale/untrustworthy subagent UI state. The exact Desktop build was not captured; private goal/repository names are omitted.
Additional Windows evidence with a direct correlation between the stale UI card, the child's terminal rollout event, persistent SQLite state, and the renderer's lazy
thread/readreconciliation.Environment
26.721.4979.0codex-cli 0.145.010.0.26200019f63c1-aa87-73b1-84ad-a52bbdb047d2No artificial reproduction was run. This evidence comes from the existing affected thread, other existing sessions,
state_5.sqlite, rollout JSONL files,logs_2.sqlite, and Desktop renderer logs.Screenshot timeline
Two screenshots were captured immediately before interacting with the stale card:
2026-07-26 16:36:27local: the Subagents summary showed 1 working / 40 done (1 w toku / 40 Gotowe).<img width="506" height="136" alt="Image" src="https://github.com/user-attachments/assets/9919f7d2-b291-4cf2-8c74-82d3c1c0a3c2" />
2026-07-26 16:36:32local: the only active card was Bench model audit, status Working (Pracuje), elapsed 7d 14h 57m 7s.<img width="1262" height="163" alt="Image" src="https://github.com/user-attachments/assets/548f6eac-e834-4cea-aad4-5191711c7a0a" />
The displayed elapsed time matches the child's creation timestamp almost exactly. The corresponding child was created locally on July 19 at 01:39 and had actually reached
task_completeat 01:44. The UI timer therefore appears to have continued measuring from creation while relying on stale active state, rather than reflecting a recent heartbeat or model request.Direct lifecycle-state evidence
Current
thread_spawn_edgesstate:| Status | Count |
|---|---:|
|
open| 2,067 ||
closed| 28 |Age of children whose edge is still
open, based on the child thread's last update:| Last child update | Count still
open||---|---:|
| More than 1 hour ago | 2,067 |
| More than 1 day ago | 2,057 |
| More than 7 days ago | 1,689 |
| More than 30 days ago | 13 |
The affected parent alone has 35 child edges, all persisted as
open.Representative completed children from that parent:
| Child | Nickname | Terminal event | Edge |
threads.tokens_used||---|---|---|---|---:|
|
019f63c3-b2d2-71c1-8ffe-05528872f15b| Kierkegaard |task_completeat2026-07-15T03:23:51.832Z|open| 25,285,561 ||
019f7799-4beb-7732-8eb0-f9de4ed5bb69| Turing |task_completeat2026-07-18T23:44:24.795Z|open| 188,044,240 ||
019f8abe-4efd-7d32-a307-9803cbf7f48c| Kepler |task_completeat2026-07-22T16:56:14.093Z|open| 245,676,686 |Each rollout ends normally with:
A correctly
closedchild inspected for comparison ends with the same sequence. The terminal rollout event therefore does not reliably propagate into the durable spawn-edge status.This is not isolated to one parent. For the eight parent threads with the largest cumulative token counts under
openchildren, I inspected the highest-tokenopenchild from each. Seven of the eight rollouts ended withtask_completein their final lines while the persistent edge remainedopen.Lazy reconciliation of the seven-day-old card
The child matching the stale screenshot is Turing:
At the moment the card was interacted with, the Desktop log recorded:
The card then moved from working to completed. There was no
thread/resumeorturn/startfor the child around this interaction, and its rollout andupdated_atdid not change. This strongly suggests that the renderer initially used stale lifecycle state and reconciled the child only after a lazy per-cardthread/read.An earlier parent-view activation also showed
resumeState=needs_resume, followed bythread/resume, and thenmaybe_resume_successwith bothlatestTurnStatus=completedandmarkedStreaming=true. No model turn started in that sequence, but it demonstrates ambiguous rehydration state that may explain why terminal children initially render as working.Token-volume evidence and limitation
The 35
openchildren under this parent have a combined:This is almost exactly five billion raw cumulative tokens. It is not a billing statement and does not prove post-completion execution. For the child observed during the card interaction, the logs show no new model turn or rollout write.
So the evidence confirms stale lifecycle state and lazy UI reconciliation, but does not confirm that hovering or loading the card re-executed the child. The high historical volume may have accumulated during the original runs and been amplified by repeated long-context/model-cycle behavior described in #35050 and #32503. Internal request/quota telemetry is needed to determine whether any completed child ID was scheduled after its original
task_complete.Expected behavior
task_completeshould atomically transition the durable parent/child lifecycle out ofopen, or persist a distinct terminal state rendered as completed.thread/read.workingshould only be shown when the backend confirms a live child handle with an active turn.thread/resume.Related: #35052 corroborates the Windows UI-state ambiguity; #25426 demonstrates durable/live registry divergence; #17832 links incomplete subagent teardown with leaked MCP processes and memory. #35050/#32503 concern a different layer and are best treated as possible cost amplifiers, not the root cause of the stale
openedge.Additional Windows reproduction from Codex Desktop 26.803.5235.0.
Environment:
Observed behavior:
This is strong evidence of renderer/parent-tree lifecycle state diverging from authoritative child thread state. It also demonstrates that the displayed timer is age since creation, not proof of a live turn or heartbeat.
Expected:
No evidence of post-completion token execution was observed; the defect is that the UI presents completed/notLoaded children as actively working. Screenshots of the 22, 21, and 8 Active states are available if maintainers need them.
Additional macOS Desktop evidence showing that durable
openstate is overwhelmingly stale and does not represent live subagent execution.Environment
26.730.61639(build6234)0.147.0-alpha.1.226.5.2(25F84), arm64Current durable lifecycle state
The active
state_5.sqlitecontains 644thread_spawn_edgesrows:| Edge state | Count |
|---|---:|
|
closed| 382 ||
open| 262 |Joining those edges to the child thread's last update time:
openchildren had no update for more than 1 houropenchildren had no update for more than 24 hoursopenchildren had any update in the previous hourAt sampling time, the running Codex app-server had only four subagent rollout files open, despite 262 durable edges being marked
open.Three representative long-lived parents showed:
closed+ 28open; all 28 open children were stale for more than 1 hourclosed+ 15open; 9 of the open children were stale for more than 1 hourclosed+ 8open; all 8 open children were stale for more than 24 hoursThe Desktop catalog reported the first two parents as
activeand the third asnotLoaded. From the user surface, many historical children remain listed while attempts to steer, message, or close them are frequently unavailable or unsuccessful. The state distribution confirms that this is not just a large amount of legitimate concurrent work.Expected behavior
open.The same audit found that stale/closed child rollout retention had grown active session storage to 405 GiB. The sanitized storage attribution and fork amplification evidence is here: https://github.com/openai/codex/issues/30779#issuecomment-5229666803
No transcript content, project identifiers, thread IDs, task titles, or machine-specific absolute paths are included.
One important clarification about the causal loop behind the large stale-agent count: the workflow is explicitly instructed to prefer reusing an existing subagent instead of spawning a new one. The high count is therefore not intentional fan-out.
The observed sequence is:
open, even though it is not doing useful work.This explains why a single long-lived parent can reach roughly 100-200 child entries despite having a reuse-first policy. It also links the lifecycle bug to the storage amplification reported in https://github.com/openai/codex/issues/30779#issuecomment-5229666803: every forced replacement can materialize another large child rollout while the previous stale child remains retained.
Expected behavior:
No child IDs, parent IDs, task names, prompt text, or transcript content are included.
A second direct reproduction path is worth recording because it does not depend on passive UI restoration:
This shows the defect is not only stale rendering after restart. It is also reproducible through an explicit user-initiated cleanup/close command from the owning parent thread. The parent has enough durable history to display the children, but apparently lacks authoritative live handles or an idempotent reconciliation path to close them.
Expected behavior for a main-thread cleanup command:
This is the same reuse/close failure loop described in my previous comment, now with an explicit parent-thread cleanup command as the trigger. No task text, IDs, project data, or transcript content are included.
Additional quantitative Windows evidence from Codex Desktop
26.803.5235.0.This was collected read-only from an actively running, long-lived Goal. I did not edit SQLite/session files, restart Desktop/app-server, archive children, or interrupt the Goal.
Authoritative current state
The parent had just started exactly three fresh read-only acceptance subagents. Direct
thread/readresults showed all three child threads with currentinProgressturns. The parent's current turn also contained exactly three correspondingsubAgentActivity(kind=started)items.Durable edge state for the same parent
state_5.sqlite.thread_spawn_edgescontained 279 child rows for that parent, and all 279 were stillstatus = 'open'.Joining every edge to its child rollout and inspecting the tail produced this exact partition:
| Child rollout state | Count |
|---|---:|
| terminal
task_completepresent | 257 || terminal
turn_abortedpresent | 19 || no terminal marker | 3 |
The three rollouts without a terminal marker were exactly the same three children currently reported
inProgress. Therefore:open: 276Additional durable-state facts:
Projection evidence
Before the three current children were spawned, recent full
world_statesnapshots hadstate.environments.subagents = null. The next three snapshots advanced cleanly from one to two to three named subagents as the fresh children started. This indicates the current environment projection can represent the real live set accurately, while the durablethread_spawn_edges.status = openpopulation remains unreconciled and can still drive an incorrect Active/Working UI count.Suggested owning-layer invariant
task_complete, aborted/interrupted/stopped) must idempotently transition its spawn edge out ofopen.inProgressturn; historical edge presence alone is insufficient.This dataset cleanly distinguishes stale UI/durable lifecycle state from actual ongoing subagent work: the only three non-terminal rollouts are the only three currently live children.
Corroborating this on Windows with a concrete UI/backend state divergence.
Environment
26.803.5235.0Observed
Persisted-session evidence
spawn_agent: 117wait_agent: 827send_message: 214followup_task: 74interrupt_agent: 13list_agents: 205Impact
Expected
This appears to be persisted task-state/UI reconciliation rather than currently executing orphan processes.
One additional restart/reconnect reproduction may help narrow the owning layer.
Observed sequence
This complements the live-registry divergence reported above and suggests that startup reconciliation is incremental and can be interrupted before reaching a stable terminal projection.
Expected behavior:
Windows corroboration (Codex App 26.803.10989.0)
I can reproduce the stale Active-list state on Windows 11 in a long-running coordination session.
Observed
pending_init.pending_initrecord returnedprevious_status: pending_init, but the record remainedpending_initand the UI count did not reconcile.Expected
The attached screenshot is from the affected session. Repository contents, credentials, source code, and private payloads are omitted.
<img width="3440" height="1398" alt="Image" src="https://github.com/user-attachments/assets/808865dd-2e21-4841-bace-48986020610a" />
Additional macOS Desktop data point, likely the same lifecycle/UI reconciliation family.
Environment:
Observed:
not_found.Impact:
Expected:
I can provide redacted screenshots of the 52-active pane and the control-plane mismatch if useful.
Additional reproducible evidence from Codex Desktop 26.803.61601 (build 6396), macOS 26.5.2 arm64:
thread_spawn_edges, all still markedopen.open, even though the live API reported those four asinterrupted.thread_spawn_edges.statusvalue remainedopen.This makes the drift concrete: child lifecycle/archive state and the parent-child edge status are not reconciled. A counter derived from open spawn edges can therefore continue reporting completed/interrupted/archived agents as working.
Expected: completion, interruption, or successful archival should transactionally close the corresponding spawn edge, and loading a parent task should reconcile stale open edges against current child state.
Windows follow-up: stale Working UI persists with zero open spawn edges
This adds a second failure mode to the open-edge drift described above.
Environment:
26.803.10989.0After a full application exit and relaunch (confirmed by fresh Codex process start times), the Subagents panel still displayed 39 historical children as Working.
A read-only post-restart check of persisted lifecycle state showed:
thread_spawn_edges: closed=335, open=0So stale
openspawn edges are one reproducible cause, but they are not sufficient to explain every stale Active/Working list. In this Windows case, every persisted edge was already terminal and the stale UI survived a clean restart.This suggests the panel can also be rehydrated from another historical projection (for example rollout/world-state replay or renderer state) without applying the terminal edge state as the authoritative result.
Expected: on parent-task load, if the authoritative lifecycle store and live registry both contain no open child, the Active/Working list must be empty; historical children may remain only under Done/history.
No local paths, task IDs, repository contents, credentials, transcript contents, or project payloads are included. No cache, SQLite, rollout, transcript, history, or repository file was changed for this follow-up.
Additional macOS reproduction: 76 Active in UI, zero running children in live registry
This appears to be the same lifecycle/UI reconciliation defect.
Environment
26.803.61601(build6396)codex-cli 0.147.0-alpha.6.526.5.1(25F80), Apple Silicon (arm64)Observed
Persisted-state evidence (read-only)
For the affected parent,
thread_spawn_edgescontains:82direct child edges;82still markedopen;81children with no thread update for more than 1 hour;78children with no thread update for more than 1 day;0archived child rows.The UI's 76 Active count therefore does not match either the live registry (0 running children) or the direct persisted-open count (82). No SQLite/session files were edited, no processes were killed, and no cleanup workaround was attempted.
Possible usage impact — correlation, not proven causation
The account's weekly Codex usage allowance has appeared to deplete unusually quickly over the last couple of days. I cannot establish that stale UI entries caused this: UI timers are not inference telemetry, and local persisted token counters are not billing records. It would be useful for OpenAI to correlate the affected parent/child lifecycle with server-side request and quota telemetry and determine whether any child received model work after its terminal event.
Expected
Active / Workingshould require an authoritative live handle with an in-progress turn.Screenshots are available, but are not attached here because their task titles contain private project context. A redacted version or private feedback session can be provided if useful.
Additional Windows reproduction: 17 ghost cards, backend has zero handles
I can reproduce this issue on the current Windows Codex Desktop app with a stronger UI/backend mismatch.
Environment
OpenAI.Codex_26.820.7780.0_x64ChatGPT.exe151.0.7922.17022631Reproduction and observed state
world_state.payload.state.environments.subagents.not-foundfor 17/17 IDs.not-found.navigated=true.None of the visible cards had a live “working” indicator, but they remain in the ordinary Subagents list rather than a separate historical section. No SQLite or rollout files were modified.
Expected
close_agent(id)returnsnot-found, that exact ID should not remain in the active/open list.Additional diagnostic clue
The displayed ID/name pairs remain in historical rollout
world_state ... environments.subagentssnapshots even though the live agent registry no longer resolves them. This suggests the UI may be rendering a persisted historical relationship/snapshot without a terminal reconciliation event.Potential areas:
world_state.environments.subagentsreconciliation;not-found) not generating a UI-removal lifecycle event;Impact
The user cannot trust the list to determine whether work or concurrency slots remain active, and repeated supported close attempts cannot repair the UI. This has occurred multiple times in the same long-running parent task.
I have retained a sanitized local report and two screenshots. Full task IDs, agent IDs, repository details, prompts, and local paths are intentionally omitted from this public comment, but can be supplied through a private maintainer channel if useful.
Suggested regression test: spawn/complete/close multiple waves, assert backend count zero, persist/reload/navigate parent, and assert the active Subagents section is empty—including the case where a repeated close returns
not-found.Additional macOS reproduction: UI shows 180 Working while live registry has no child running
Environment:
26.818.61809(build7019)codex-cli 0.149.0-alpha.4.327.0(26A5416b), Apple Silicon (arm64)Observed:
completed / DONE, onecompleted / BLOCKED.Read-only persisted-state evidence:
thread_spawn_edges: 861 total, 714 open, 147 closed.Assessment:
This appears to be a lifecycle reconciliation defect. Archived and terminal child threads can retain
thread_spawn_edges.status = open, and the UI renders a third inconsistent Working count. This is UI/accounting state, not evidence of active model work.Expected:
The current desktop automation interface explicitly refuses to control the Codex app itself, so I could not submit the same report through the in-task
/feedbackdialog. This public comment contains only sanitized counts and version information; no task IDs, prompts, repository content, credentials, logs, or database files are included.Still reproducible in Codex Desktop on macOS as of 2026-08-26.
In a long-running project task that repeatedly spawned bounded review subagents:
agent not found, indicating no live manager object remained;close, but no supported list/delete/remove/prune/GC operation for stale historical child records.This leaves the UI and live manager state demonstrably out of sync. The user has encountered the accumulation repeatedly over a long period and is specifically concerned about memory/resource degradation from retained subagent records. We did not measure RAM or disk growth in this reproduction, so that part is reported as impact/concern rather than a confirmed causal measurement.
Requested behavior:
No private repository content or task/agent IDs are included here.