Forked session is hidden from resume picker until its first new message

Open 💬 0 comments Opened Aug 19, 2026 by bioinformatist

Codex version

codex-cli 0.148.0

Platform

Linux x86_64, terminal TUI

Problem

A newly created fork does not appear in the codex resume picker until the user sends a regular message in the fork.

The fork is already durable and directly resumable before that message. It has a rollout file, a row in state_5.sqlite, the expected cwd, source = cli, and archived = 0. Renaming the fork also succeeds and persists the name. Direct lookup works:

codex resume <fork-thread-id>

However, neither of these lists it:

codex resume
codex resume --all

Changing the picker sort order or cwd filter does not help. As soon as one regular user message is submitted in the fork, it appears in the picker.

Reproduction

  1. Open an existing interactive CLI session with conversation history.
  2. Run /fork, or start a fork with codex fork <thread-id>.
  3. Optionally run /rename in the new fork. Do not send a regular message.
  4. Note the new thread ID from /status.
  5. In another terminal, run codex resume --all.
  6. Observe that the fork is missing from the picker.
  7. Run codex resume <fork-thread-id> and observe that the fork opens successfully.
  8. Submit one regular user message in the fork.
  9. Run codex resume --all again. The fork is now listed.

This reproduced twice with fresh forks. The second reproduction was done after all older Codex processes had exited, so mixed CLI versions were ruled out.

Local evidence

Before the first message, the fork had already been written to the normal persistence surfaces:

threads.id             = <fork-thread-id>
threads.source         = cli
threads.thread_source  = user
threads.archived       = 0
threads.history_mode   = paginated
threads.name           = <persisted rename>
threads.rollout_path   = <existing rollout file>
threads.preview        = ''
threads.has_user_event = 0

The distinguishing field is the empty preview. In codex-rs/state/src/runtime/threads.rs, ordinary thread listings add:

AND threads.preview <> ''

The resume picker uses this listing path. A direct UUID resume uses thread/read, so it bypasses that filter. Sending the first regular message fills the preview and makes the fork visible.

Expected behavior

A persisted fork that can be resumed by UUID should appear in the resume picker before it receives a new message. This is especially important after an explicit rename: the user has created and named a branch of an existing conversation, not an accidental empty session.

The empty-preview filter is useful for brand-new sessions that were opened and abandoned without input, but a fork already contains inherited conversation history. It should not be treated as an empty disposable thread.

Possible fix

The listing policy could include persisted forks even when their preview is empty, rather than removing the empty-preview filter globally. Another option is to give a fork a display preview derived from its inherited history, although that may blur the distinction between inherited content and the first message created in the fork.

This is related to #21619 because both failures end at the empty-preview listing filter, but the trigger is different. #21619 concerns an existing rollout whose first user event is beyond a bounded scan. This report is a deterministic fresh-fork case: persistence and rename succeed, but the child has no child-owned user event yet.

We would be happy to prepare a PR with a focused regression test and fix if maintainers agree on the desired listing semantics.

View original on GitHub ↗