thread/list hides valid sessions when the first preview event is beyond the rollout head scan window
Summary
thread/list can omit valid, non-archived sessions when the default listing path has metadata filters and the rollout file's first preview-bearing event is beyond the limited head scan window.
This surfaced in the iOS remote Codex app as several recent sessions being impossible to find, even though the sessions were present in state_5.sqlite, had valid cwd, source, model_provider, preview, and were returned correctly when useStateDbOnly: true was used.
Environment
- Codex Desktop:
26.513.20950 - Codex CLI bundled with desktop:
0.131.0-alpha.9 - iOS remote client observed in app-server logs:
codex_chatgpt_ios_remote 1.2026.125 - Platform: macOS, app-server
thread/listAPI v2
What happens
A normal thread/list request like this can omit affected sessions:
{
"method": "thread/list",
"params": {
"sortDirection": "desc",
"limit": 25,
"sortKey": "updated_at",
"sourceKinds": []
}
}
The same request with useStateDbOnly: true returns the sessions correctly.
Root cause from code inspection
In codex-rs/app-server/src/request_processors/thread_processor.rs, when modelProviders is omitted, the app-server defaults to the configured model provider. That means the downstream rollout listing is treated as metadata-filtered.
In codex-rs/rollout/src/recorder.rs, metadata-filtered listings return the filesystem-backed page rather than the SQLite page:
let listing_has_metadata_filters = !allowed_sources.is_empty()
|| model_providers.is_some()
|| cwd_filters.is_some()
|| search_term.is_some();
For the filesystem-backed page, codex-rs/rollout/src/list.rs uses read_head_summary() and only scans HEAD_RECORD_LIMIT + USER_EVENT_SCAN_LIMIT lines while looking for a preview-bearing event_msg.
If a large or forked/migrated rollout has valid session_meta at the top but the first event_msg.user_message appears after that scan window, build_thread_item() returns None because summary.preview is missing:
if summary.saw_session_meta && summary.preview.is_some() {
return Some(ThreadItem { ... });
}
None
The session is then excluded from the returned list, despite SQLite having a good preview and correct metadata.
Minimal reproduction shape
- Have a valid active rollout file with
session_metanear the top. - Put enough non-preview lines after
session_metaso the firstevent_msg.user_messageappears afterHEAD_RECORD_LIMIT + USER_EVENT_SCAN_LIMITlines. - Ensure SQLite has correct metadata for the session, including non-empty preview, source
cli/vscode, model provideropenai, and archived false. - Call
thread/listwith default settings or with source/model filters. - The session is omitted.
- Call the same request with
useStateDbOnly: true; the session appears.
Expected behavior
A session that is valid in the state DB and matches the listing filters should not disappear just because the filesystem preview scan did not find an early event_msg.user_message.
Actual behavior
The filtered list path returns the filesystem scan page, and the filesystem scan silently drops rollout files whose preview event is too far from the head. iOS remote then cannot list or open those sessions from the project/session list.
Workaround
Adding a small preview-bearing event_msg.user_message near the top of the rollout file makes the affected sessions appear again. This confirms the issue is the head-preview scan, not SQLite metadata or archive/source filters.
Suggested fix
A minimal fix would be one of:
- When filesystem
ThreadItemlacks preview, overlay/fill preview from the state DB before deciding to drop it. - For metadata-filtered listings, prefer the DB page when the DB row exists and has valid preview, using filesystem scan only for repair/staleness checks.
- Increase or adapt the preview scan so valid sessions with large inherited/forked heads are not excluded.
The most robust option seems to be avoiding preview as a hard filesystem-only eligibility condition when SQLite already has valid preview metadata.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗