Windows: `thread/archive` fails with "os error 2" after `thread/resume` stores a `\\?\` (verbatim) rollout_path — same file queued twice
Open
Nobody has claimed this yet.
app
app-server
bug
session
windows-os
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
Summary
On Windows, once a thread has been resumed, archiving it fails with:
failed to archive session: thread-store internal error: failed to archive thread: The system cannot find the file specified. (os error 2)
The rollout file exists and is readable. Root cause is a path-equality mismatch in archive_thread_with_paths that causes the same rollout file to be renamed twice.
Environment
- Codex desktop 26.814.5167.0 (release 26.814.41407), bundled CLI 0.148.0-alpha.15
- Windows 11 (build 26200)
Repro
- Open (resume) any existing thread in the desktop app.
threads.rollout_pathinstate_5.sqliteis now stored as\\?\C:\Users\<user>\.codex\sessions\...\rollout-...jsonl(verbatim prefix — looks likestd::fs::canonicalizeoutput). Threads that have not been opened keep the plainC:\Users\<user>\.codex\sessions\...\rollout-...jsonlform.- Archive that thread → error above. The file is still in
sessions/afterward. - Workaround that confirms the diagnosis: strip the
\\?\prefix from that row'srollout_path, then archive without re-opening → succeeds. Re-opening the thread re-introduces the prefix and the failure.
Analysis (codex-rs/thread-store/src/local/archive_thread.rs)
In archive_thread_with_paths:
rollout_pathscomes fromowned_rollout_paths_from_index(&reference_index, ...)— a filesystem scan → plainC:\...paths.selected_rollout_pathcomes fromthread_rollout_resolver::resolve_current→ the SQLite row →\\?\C:\....if !rollout_paths.contains(&selected_rollout_path) { rollout_paths.push(selected_rollout_path) }comparesPathBufs byte-wise, so the verbatim and non-verbatim forms of the same file are treated as different and both are queued.- Each is then passed through
scoped_rollout_path(helpers.rs), which canonicalizes both to the identical source and computes the identical destination. - First
std::fs::renamesucceeds; the second fails withNotFoundbecause the source was just moved.restore_rollout_movesthen rolls the first move back — hence the file "still exists" and the misleading error.
Suggested fix (either/both)
- Dedup
rollout_movesafter canonicalization (compare canonical source paths, or dedup by rollout id/filename) rather thancontainson mixed-form paths beforehand. - Don't persist verbatim (
\\?\) paths on resume; store the same form the scanner produces (e.g.dunce::canonicalize, or strip the verbatim prefix on Windows).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in codex-rs/thread-store/src/local/archive_thread.rs at archive_thread_with_paths, then read scoped_rollout_path in helpers.rs and the thread_rollout_resolver path handling. Reproduce the Windows resume-then-archive flow and verify that the same rollout is queued only once and archiving completes without restoring a failed move.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust, sqlite
- Domain
- cli, operating-systems
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100