Subagent child threads are counted as top-level recent conversations and can leave stale open spawn edges
Summary
Subagent child threads appear to be persisted as normal threads and returned by the ordinary recent conversation listing. When many subagents have been used, those child threads consume the global recent-conversation slots, so older real parent/user conversations look like they have disappeared from the sidebar even though they are still present locally.
There also appears to be stale lifecycle metadata: some rows in thread_spawn_edges can remain open long after the corresponding subagent work appears inactive.
Environment
- Platform: Linux
- Client: VS Code extension / desktop-style Codex UI
- Local state DB inspected:
~/.codex/state_5.sqlite
Anonymized Local Evidence
From one local state DB:
- Total threads: 553
- Unarchived threads: 114
- Subagent child threads linked by
thread_spawn_edges: 61 - Unarchived subagent child threads: 51
- Unarchived non-child threads: 63
- Parents with subagents: 14
thread_spawn_edges.status: 56closed, 5openjobs,agent_jobs, andagent_job_itemswere empty- Process inspection did not show many active subagent workers, only the normal app-server process
No private thread titles, paths, or thread IDs are included here.
Queries Used
select status, count(*)
from thread_spawn_edges
group by status;
select c.archived, count(*)
from thread_spawn_edges e
join threads c on c.id = e.child_thread_id
group by c.archived;
with child as (
select child_thread_id from thread_spawn_edges
)
select 'unarchived_threads', count(*) from threads where archived = 0
union all
select 'subagent_children_unarchived', count(*) from threads where archived = 0 and id in child
union all
select 'normal_unarchived_excluding_subagent_children', count(*) from threads where archived = 0 and id not in child;
Observed Behavior
- Use subagents over time so child threads are created.
- The child threads are stored in
threadsand linked fromthread_spawn_edges. - Many child threads remain
archived = 0. - The recent conversation list includes those child threads as ordinary top-level conversations.
- These child threads consume recent-list slots, making older parent/user conversations look missing from the sidebar.
- Some spawn edges remain
openeven when there is no obvious active agent job or worker process.
Expected Behavior
- Subagent child threads should not be surfaced as top-level recent conversations by default, or should be grouped under the parent thread.
- Subagent child threads should not consume the global recent-conversation page limit.
- Completed/inactive subagent edges should reliably transition to
closed, or staleopenrows should be ignored/cleaned up. - Sidebar/search behavior should keep real parent/user conversations discoverable even when many subagents exist.
Why This Matters
In the inspected state, 51 unarchived child threads inflated the recent-list population while the actual unarchived non-child conversation count was only 63. This interacts badly with recent-conversation pagination/capping and makes real conversations appear to disappear.
Possibly Related Issues
- #15368
- #19197
- #16817
- #21128
This may be the connection between the recent-list cap and subagent lifecycle/state persistence: child subagent threads are counted as normal recent conversations, and some spawn edges remain stale-open.
10 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
I added this child-thread/recent-list/spawn-edge drift case to
trace-to-skill@0.1.66:Not a Codex fix; it is a public fixture + report checklist for the exact state split this issue describes: child threads showing as top-level recent conversations, archived/unarchived child state,
thread_spawn_edgesopen/closed counts, parent/root thread id, subagent ids/roles, list/close/readback behavior, quota/max_threads evidence, and whether stale children are only UI noise or still consume spawn slots.Release: https://github.com/grnbtqdbyx-create/trace-to-skill/releases/tag/v0.1.66
Additional current-build evidence from a Windows + WSL2 Desktop profile. This looks like the same bug class, but with a larger state split and a slightly different user-visible symptom: the subagent rows are not visible as normal sidebar chats, yet they remain hot in
threads.archived = 0and appear to affectthread/list.Environment:
OpenAI.Codex_26.609.9530.0_x64__2p2nqsd0c76g0state_5.sqliteSanitized state counts:
Archived timestamp consistency:
Most unarchived subagent rows are from one WSL project / parent workflow:
Observed behavior:
thread_source = 'subagent' AND archived = 0for 4,425 rows.thread/listis still very slow. In the latest two Desktop log files I found 97thread/listcalls withdurationMs; all were over 2 seconds, with a max around 84 seconds and top values around 61s, 56s, 44s, 37s, 28s, 23s.Attempted local mitigation:
thread_source = 'subagent' AND archived = 0rows timed out while the Desktop app was running and the WAL was active.Expected behavior:
thread/list/sidebar metadata queries.threads.archivedstate should not diverge this way.state_5.sqlite.Related issues I found while checking for duplicates:
Follow-up: after quitting Desktop fully, archiving only
thread_source='subagent' AND archived=0updated 4,425 rows.Later state showed:
That confirms the hidden subagent rows were still in the hot unarchived thread population and could be moved out safely only while Desktop was stopped. The remaining product gap is a supported cleanup/repair path and preventing completed hidden subagents from accumulating as
archived=0.Additional current-build evidence from Codex App on macOS. This is the same user-visible bug, with a more specific metadata mismatch that explains why some delegated child threads are returned as top-level conversations.
Environment
26.707.61608(build5200)codex-cli 0.144.0-alpha.4Darwin 25.5.0 arm64 arm~/.codex/state_5.sqlitePRAGMA integrity_check:okSanitized persisted shape
One Codex Desktop-created delegated child was persisted identically in SQLite and the rollout
session_metaas:Its first user item is a
<codex_delegation>envelope containing a parent/source thread id, so it is a delegated child rather than an ordinary user-created conversation. However, the canonical parent relation is absent and itsSessionSourceisVSCode, notSubAgent::ThreadSpawn.In the same DB:
thread_source = subagent AND source IN (cli, vscode)SubAgent::ThreadSpawn/ other subagent sources and do not have this shapeWhy these rows enter the top-level list
Current upstream
compute_source_filters(None)defaults toINTERACTIVE_SESSION_SOURCES, andsource_kind_matches(VsCode)matchesThreadSourceKind::VsCode. The separatethread_source = subagentclassification is not used to exclude the row:The result is that these delegated children appear as normal top-level project conversations and consume recent-list slots.
Expected fix
Prefer persisting Desktop-created delegated children with the native
SessionSource::SubAgent(SubAgentSource::ThreadSpawn { parent_thread_id, ... })plus the canonical parent relation. As a defensive fallback, top-level sidebar queries could also exclude rows classified asthread_source = subagentwhen no parent/ancestor relation filter is requested.This evidence was collected read-only; the DB and rollout files were not modified.
Fresh Windows/Desktop reproduction (2026-08-06) that appears to confirm and amplify this issue:
Environment
26.730.8199.0codex-cli 0.147.0-alpha.1.2Observed state
thread_spawn_edgeshad 1,313 open and 1,689 closed rows.task_completeand 18 inturn_aborted.After a backup-first reconciliation reduced the database to zero open edges, newly completed subagents immediately reproduced the problem: six child rollouts ended in
task_completebut their spawn edges remainedopen.Tool-surface A/B
I tested the documented stable feature separately:
features.multi_agent = true.features.multi_agent_v2 = false.Result:
close_agentwas still absent from the Desktop task's callable collaboration-tool inventory, while other collaboration controls remained available. Newly completed children still remained open. The configuration was restored afterward.This may connect the stale-edge behavior here with #33590, although in this reproduction collaboration tools were partially available rather than the entire v1 namespace being absent.
Requested safeguards
closedwhen the child rollout reachestask_complete.close_agentoperation in the Desktop collaboration tool catalogue.Database integrity checks passed and no rollout histories were deleted during the local reconciliation.
Fresh Windows/Desktop reproduction: completed parent disappears after 27-child run, with stale parent recency and ~9.9k unknown-child events
This adds an end-to-end user-visible reproduction to the child-thread/recent-list problem: a completed parent task disappeared from its project task view, while the full conversation remained recoverable by exact ID.
Environment
2620026.730.8199.0codex-cli 0.147.0-alpha.1.2Observed sequence
status=completed; the rollout ends intask_complete.archived=0, its rollout JSONL was complete, and an exact-ID read/open succeeded. Opening it directly made it visible again.Timestamp drift
The durable indexes stopped aging the parent while its long turn continued:
The SQLite timestamp is effectively the long turn's start time, not its completion time. By completion, 32 rows had newer recency values: 26 subagent and 6 user rows. This makes the completed parent vulnerable to recent-list/project-view displacement even though it is the work the user just finished.
Renderer/conversation-state evidence
During the same parent turn, Desktop logged:
Of these, 9,877 events mapped directly to all 27 child IDs. The parent conversation itself had zero error-severity log entries and completed normally.
At parent completion, Codex also failed to retain the optional turn-diff checkpoint:
The repository working state and complete parent history remained intact, so this checkpoint failure was not data loss, but it occurred at the same completion boundary where parent metadata should have been refreshed.
Assessment
This looks like the combination described in this issue:
updated_at/recency_atis not refreshed during or after a long multi-agent turn;This also provides durable-state evidence for the UI symptom in #34125 and overlaps the unknown-child event path in #32738.
Expected safeguards
Raw logs, task IDs, titles, prompts, and local paths are omitted for privacy. I can provide narrowly scoped redacted excerpts if maintainers request them.
Follow-up on the same reproduction: the recovered parent disappeared again immediately after a direct-ID navigation/open.
This produced a useful recovery A/B:
archived=0and still reportedis_pinned=0.So merely opening a recoverable hidden parent does not refresh/persist its project-list state or stale recency. Explicit pinning is currently a viable workaround, but users should not have to know the hidden thread ID or pin every affected parent. This strengthens the case for refreshing parent recency/visibility on direct open and terminal completion.
Windows recurrence: five completed user parents omitted; direct open did not restore them; pinning was required
This is a broader recurrence of the behavior reported in comments 5236953035 and 5236967525.
Environment
26.730.8199.0codex-cli 0.147.0-alpha.1.226.803.10989.0PRAGMA integrity_check:okRecovery A/B
list_threads(limit=50); all five werearchived=0andthread_source=user.The intervening Desktop upgrade/restarts did not self-heal list membership. This reproduces the earlier direct-open-versus-pin distinction across five roots and three unrelated projects.
Parent recency and child-lifecycle drift
For all five missing roots,
session_index.jsonl.updated_atstopped before the terminal completion event—by approximately 2m33s, 43m, 1h17m, 2h45m, and 2h50m. SQLiterecency_at_mswas likewise near initial activity rather than terminal completion.During the core overnight creation window, 77 new thread rows were added:
Current profile state remains hot with completed children:
Four of the five missing roots owned 26 direct child edges. Every one of those 26 child rollouts ends in
task_complete, yet all 26 edges remainopen. The fifth missing root had no children, which suggests global child/list contamination can displace an unrelated root too.Log correlation
Across 63,928 Desktop log lines in the overnight window:
There were zero instances of the previously observed GPU-crash, browser-renderer-gone, code-mode-host-exited, or stdio-transport-closed signatures. Windows Application/WER also had no Codex fault for this window. The AppX update/restart occurred only after the overnight tasks had completed.
So this incident is not transcript loss and does not appear to be another native crash. It is a persistent discoverability/indexing failure with stale parent recency, completed-but-open child edges, and large volumes of unregistered child conversation events. Pinning is currently an effective recovery workaround when the hidden exact IDs can be found.
Requested safeguards
closedwhen their rollouts reachtask_complete, and reconcile completed-but-open edges at startup.updated_at/recency_atat terminal completion and after successful direct navigation.Task IDs, titles, prompts, project names, paths, usernames, and raw logs are omitted for privacy.
Hi @Zanekingd, your report looks related to a local session discovery/inventory gap rather than necessarily a lost rollout. I've released Codex Rescue Alpha5, a local read-only diagnostic tool that performs filesystem-first discovery across active and archived rollouts and cross-references SQLite/index metadata without modifying any state.
If you still have the affected local Codex directory, you can inspect it via:
No raw files or databases are needed. If you choose to share output, please review and sanitize any private repository paths or names first.