TUI /resume can show No sessions yet when cwd-filtered thread/list drops state DB matches
Summary
The TUI /resume picker can display No sessions yet for the current working directory even when matching sessions exist in the local Codex state DB and can still be resumed directly by ID.
In the local repro, the same session was:
- visible in local history/state metadata,
- resumable with
codex resume <session-id>, - returned by
thread/listwhenuseStateDbOnly: truewas used, - but omitted by the default scan-and-repair
thread/listpath used by the TUI/resumepicker with a cwd filter.
This appears related to #22037 and the lower-level issue described in #22777, but the visible CLI/TUI failure mode here is a correctness issue: the picker says there are no sessions, not only that listing is slow.
Environment
- Codex CLI: observed on
0.139.0 - Surface: CLI/TUI
/resume - Platform: Linux container
- Local state layout:
CODEX_HOMEis a symlink to persistent storage
The symlinked CODEX_HOME layout does not appear to be the root cause. The relevant session files and SQLite state DB were readable, and direct resume by session ID worked. I am mentioning it only because it was part of the observed environment.
Reproduction shape
- Have existing sessions in
$CODEX_HOME/sessionswhose metadata in the state DB matches a project cwd. - Start
codexfrom that project directory. - Run
/resume. - Observe the picker shows
No sessions yetfor the cwd-filtered view. - Run
codex resume <session-id>for one of those sessions. - The session resumes successfully.
- Compare
thread/listbehavior for the same cwd filter:
- default scan-and-repair listing returns no rows for that cwd,
useStateDbOnly: truereturns the expected rows.
No raw rollout JSONL, full local paths, auth/config files, or session contents are needed to reproduce the code-path issue.
Root cause hypothesis
RolloutRecorder::list_threads_with_db_fallback currently treats source/provider/cwd/search filters as metadata-filtered listings. When the state DB is available, it queries SQLite and can see matching rows, but for metadata-filtered listings it returns the filesystem scan page rather than the DB page.
That makes valid state DB matches disappear whenever the filesystem summary scan does not produce a ThreadItem for them. One concrete way this can happen is when a rollout has valid session_meta but the first preview-bearing user event is outside the limited head scan window. In the TUI /resume cwd-filtered path, that can surface as No sessions yet even though the state DB has the session and direct resume works.
Expected behavior
For source/provider/cwd-filtered listings, if the state DB is available and contains matching valid rows, the returned first page should include those rows after reconciliation. The filesystem scan should remain a fallback/repair path, not the authoritative returned page when SQLite has already produced a valid filtered result.
Patch experiment
I tested a small patch in a fork that changes non-search metadata-filtered listings to:
- run the existing filesystem scan and repair for filesystem hits,
- query the state DB,
- fully reconcile DB-only hits so stale cwd/provider/source metadata is still corrected,
- re-query and return the repaired DB page when SQLite is available,
- keep the existing filesystem fallback when SQLite is unavailable or errors.
Patch commit:
https://github.com/Moujuruo/codex/commit/93130eeb3f9bc93999fdfd20f2dbc82568d2d1fd
I kept searchTerm behavior separate because search has its own fallback semantics when the DB has not been warmed yet.
Validation run on the patch
just fmtjust test -p codex-rollout list_threads_cwd_filter_returns_state_db_only_match list_threads_default_filter_reconciles_stale_state_db_hits list_threads_search_repairs_stale_state_db_hits_before_returningjust test -p codex-rollout(70 passed)just fix -p codex-rollout
Why this is not just a symlink problem
The local CODEX_HOME symlink was durable and readable. The same sessions could be found by ID and by state-DB-only listing. The failing path was specifically the default scan-and-repair listing used by /resume with cwd/source/provider filters returning the filesystem scan page after state DB lookup.
3 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
I checked this against current
main, and the described failure mode lines up with the code path.The chain I see is:
/resumepicker buildsThreadListParamsintui/src/resume_picker.rs::thread_list_params(...)with a cwd filter,source_kinds, anduse_state_db_only: false.RolloutRecorder::list_threads_with_db_fallback(...)inrollout/src/recorder.rsas a metadata-filtered listing becauseallowed_sources,model_providers, orcwd_filtersare present.rollout/src/list.rs::build_thread_item(...)only returns an item whensummary.saw_session_meta && summary.preview.is_some(). The preview scan is bounded byHEAD_RECORD_LIMITplusUSER_EVENT_SCAN_LIMIT, so a valid rollout whose first preview-bearing event is beyond that window will not become a filesystemThreadItem.metadata_filter, but still returnspage_from_filesystem_scan(fs_page, ...)overlaid with DB metadata. That means a valid DB row that was not present infs_pagestill cannot appear in the returned page.One existing test also shows the current policy distinction:
list_threads_default_filter_returns_filesystem_scan_resultsintentionally expects a stale DB cwd hit to disappear after reconciliation. That is a useful stale-row guard, but it does not cover the other case from this issue: a valid DB row whose rollout is readable/resumable but whose filesystem summary scan does not produce an item because preview extraction is incomplete.The regression test I would add is specifically that second case: create a rollout with valid
session_metaand a matching, non-stale state DB row, ensure the filesystem scan cannot build a preview item from the bounded head scan, then assert the default cwd/source-filtered listing returns the DB-backed row after reconciliation. That would preserve the stale-filter behavior while pinning the/resume says No sessions yet even though DB/direct resume worksbehavior.The cwd filter should run after state DB and filesystem scan results are merged.
For each candidate, keep source
state_db|jsonl_scan, stored cwd, normalized cwd/project key, and filter decision. If the DB knows a session but the scan path cannot repair it, the picker should show a degraded row with the reason instead ofNo sessions yet.---
_Generated with ax._