[Windows Desktop] Hidden subagent threads remain archived=0 and accumulate in state_5.sqlite
Summary
Codex Desktop creates internal rows with thread_source='subagent', but completed subagent rows can remain archived=0 indefinitely. They are hidden from the normal thread UI and have no user event, yet remain in the active portion of state_5.sqlite.
In one long-lived Windows Desktop + WSL profile, this accumulated to more than 4,400 hidden active rows. An offline archival update reduced that population, but additional unarchived subagent rows appeared afterward, showing that the workaround is not durable.
Environment
- Codex Desktop on Windows with the agent running in WSL
- Observed across Desktop-managed CLI versions from the
0.129through0.142series - SQLite state store:
%USERPROFILE%\.codex\state_5.sqlite
The report intentionally omits thread IDs, rollout paths, working directories, titles, prompts, repository details, and database files.
Confirmed database observations
This aggregate query is sufficient to expose the condition:
SELECT archived, COUNT(*)
FROM threads
WHERE thread_source = 'subagent'
GROUP BY archived;
Before mitigation, the result was:
archived=0: more than 4,400
archived=1: 35
All sampled aggregate groups had has_user_event=0. The active rows had no corresponding visible user chat in the Desktop UI.
Additional checks confirmed that the rows were not merely transient entries from a currently running subagent: their creation times spanned weeks, while archived remained false and archived_at remained null.
Offline mitigation and recurrence
With Desktop fully closed, after backing up the database, the following update archived the stale internal rows:
BEGIN IMMEDIATE;
UPDATE threads
SET archived = 1,
archived_at = CAST(strftime('%s', 'now') AS INTEGER)
WHERE archived = 0
AND thread_source = 'subagent';
COMMIT;
The bulk update changed more than 4,400 rows. A later aggregate check found 23 newly created subagent rows still at archived=0, plus a small number of older active rows. This confirms recurrence after cleanup.
Direct database editing is not an appropriate product workaround and requires a fully closed app plus a backup; it is included only as lifecycle evidence.
Expected behavior
When an internal subagent reaches a terminal state, Desktop/app-server should do one of the following consistently:
- mark its thread row archived and set
archived_at; - remove it if subagent persistence is unnecessary; or
- store it outside user-visible/active thread metadata paths.
Existing stale rows should receive a one-time migration or bounded cleanup.
Actual behavior and impact
- Completed internal subagent rows remain
archived=0. - The normal UI hides them, so users cannot inspect or archive them through supported controls.
- The active thread table can grow by thousands of invisible rows.
- This was discovered while investigating Desktop startup and thread-list latency. The lifecycle/storage bug is confirmed; I have not isolated a wall-clock benchmark proving how much latency these rows add.
Suggested regression coverage
- Create a parent thread that launches several subagents.
- Let every subagent reach a terminal state.
- Assert that no completed
thread_source='subagent'row remains active indefinitely. - Assert that active thread-list queries either exclude internal subagents at the SQL layer or operate on a bounded indexed set.
- Exercise cleanup across app restart and abrupt parent-thread termination.
1 Comment
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action