[Windows] Local thread archiving fails with thread-store os error 2
Open 💬 17 comments Opened Aug 18, 2026 by sudazc
Summary
On the Windows Codex desktop app, archiving a completed local thread fails with a thread-store file-not-found error. The thread remains unarchived.
Environment
- Product: Codex Desktop
- Platform: Windows x64
- App package:
OpenAI.Codex_26.814.5167.0_x64__2p2nqsd0c76g0 - Local project thread
Error
The archive request returns:
failed to archive session: thread-store internal error:
failed to archive thread: The system cannot find the file. (os error 2)
The desktop UI reports that the archive failed.
Steps to reproduce
- Open a completed local Codex thread in the Windows desktop app.
- Attempt to archive it from the thread/project UI.
- Observe the archive failure.
- Restart the desktop app and retry.
Actual behavior
- The first archive attempt shuts down the active thread instance but does not persist the archive.
- A second attempt after the thread is stopped fails with the same
os error 2. - The thread remains visible and its archive state remains
archived=0. - The rollout JSONL file exists on disk, and the
archived_sessionsdirectory also exists. - The affected rollout path uses a Windows extended-length path prefix (
\\?\\C:\\...), which may be mishandled by the archive path-move logic.
Expected behavior
A completed local thread should be archived successfully, moved to the archived-session store, marked as archived, and removed from the active thread list.
Related issues
- #36289 — Windows archive fails with a thread-store file-move error (
os error 17) - #26012 — Windows archive/restore path inconsistency with
os error 2 - #25713 — Archiving the active Windows thread causes resume/archive state problems
No raw logs, private paths, credentials, or conversation contents are attached.
17 Comments
Root-cause follow-up for the archive failure (the sidebar-cache observation is not the primary issue here).
Confirmed local evidence
OpenAI.Codex_26.814.5167.0_x64/codex-cli 0.148.0-alpha.15.thread/archivefailed repeatedly with:failed to archive thread: The system cannot find the file. (os error 2).\\?\D:\<redacted>\.codex\sessions\...jsonl..codexlocation is a Windows directory junction to that physical D: location. A separate harmless probe confirmed that moving a file through this junction intoarchived_sessionssucceeds, so the junction/cross-alias move itself is not the failing operation.Root cause
The current archive implementation can enqueue the same physical rollout twice when the path spellings differ.
RolloutReferenceIndex::scan()discovers the rollout through directory traversal using a normal (non-verbatim) path.thread_rollout_resolver::resolve_current()returns the SQLite-selected path, which in this case is the verbatim\\?\D:\...spelling.archive_thread_with_paths(), this check is lexical:if !rollout_paths.contains(&selected_rollout_path)Therefore the verbatim selected path is appended even though it refers to the same file already found by the index.
scoped_rollout_path(), which callsstd::fs::canonicalize. On Windows, both spellings resolve to the same canonical physical source.rollout_movesnow contains the same source/destination pair twice. The firststd::fs::renamesucceeds; the second attempts to move the already-moved source and returnsNotFound/os error 2.Relevant code:
Suggested fix
Canonicalize (or otherwise resolve file identity for) all candidate rollout paths before comparing/deduplicating them and before constructing
rollout_moves. The selected rollout should be matched against that canonical set, and the move list should be deduplicated by canonical source/destination. A Windows regression test should cover one physical rollout supplied once as a normal path and once as a\\?\verbatim path.For diagnostics, it would also help if a failed move logged the redacted source/destination and move index/count.
No conversation contents, credentials, or unredacted private paths are included.
I can independently reproduce this on Windows Codex Desktop 26.814.5167.0.
Local checks show:
thread/archiverepeatedly fails with:failed to archive session: thread-store internal error: failed to archive thread: The system cannot find the file specified. (os error 2)archived_sessionsexists and SQLite integrity is OK.This matches the reported Windows archive/path-normalization regression. The conversations remain readable but cannot be archived from the Desktop UI.
Additional reproduction on Windows Codex Desktop (2026-08-18).
Environment:
After fully restarting Codex Desktop, archiving still failed. The same failure was reproduced on multiple separate idle local conversations:
Additional observations:
This is consistent with the Windows local archive failure described in this issue. Please investigate archive path resolution/move handling and the final archive-state persistence path.
Independent confirmation from another affected Windows installation, with a version-isolated A/B test.
Environment
26.814.5167.026.814.414070.148.0-alpha.15Reproduction and causal isolation
\\?\C:\...path, while the JSONL file existed and was readable.thread/archiverepeatedly returned JSON-RPC-32603withfailed to archive thread ... os error 2.\\?\C:\...toC:\...while the app was fully closed was not durable: cold-resuming the legacy thread canonicalized the path back to the verbatim form before the next archive attempt.I then made an isolated copy of the affected state database and rollout subtree and ran the same app-server sequence against two valid OpenAI-signed runtimes:
Results:
0.148.0-alpha.15: fails reproducibly with the same-32603 / os error 2.0.147.0: succeeds on the identical isolated state, including the root rollout and active descendants.As a real-world confirmation, the desktop app was temporarily pointed at the signed
0.147.0runtime and fully restarted. The logs reportedcurrentVersion=0.147.0and the environment override. The previously failing thread then archived successfully in about 300 ms; it moved toarchived_sessions, disappeared from the active list, appeared in the archived list, and retained its original SHA-256.This independently corroborates the analysis above: equivalent normal and verbatim Windows paths can enter the candidate set as distinct values before canonicalization, then resolve to the same source/destination and trigger a second rename of an already-moved file. Deduplicating by canonical file identity before constructing the move list should prevent the failure.
No raw logs, private paths, credentials, thread IDs, or conversation contents are included. Sanitized timing/log excerpts can be provided privately if useful.
Additional Windows Desktop reproduction and impact:
notLoaded, but attempts to continue it reportedthread not found.thread-store ... The system cannot find the file specified. (os error 2)error even though the rollout JSONL still existed and was readable at the cataloged path.Verified recovery:
sessionsfolder toarchived_sessions.rollout_path, setarchived=1, and setarchived_at.This suggests moving a task between/out of a local project can leave the project/sidebar catalog and thread-store rollout resolution inconsistent. The archive path should reconcile an existing rollout by thread ID/basename and self-heal project-assignment metadata instead of returning
os error 2. No private conversation content, credentials, or raw local paths are attached.Comment draft for openai/codex#39130
Same issue here. Additional repro details:
thread-store os error 2).thread not found: <id>even though the.jsonlfile exists on disk and the DB row is intact (archived=0).rollout_path(and sometimescwd) stored with the Windows extended-length prefix, e.g.\\?\C:\Users\Hao\.codex\sessions\.... The desktop app rewrites the path with the\\?\prefix whenever it updates thread metadata: manually fixing the DB rows to plain paths works until the app rewrites them back.codex archive <thread-id>from the CLI archives those threads successfully (file moves toarchived_sessions, DB row updated toarchived=1).std::fs::renameand path resolution that fails on\\?\-prefixed paths on Windows (os error 2).Please fix the
\\?\-prefixed path handling (or normalize paths before rename) in the desktop archive/open code paths.I can confirm the same behavior on the current Windows Codex Desktop app with a completed voice task: the task remains readable, but the archive request fails with the same
thread-storeWindows file-not-found error and the task stays visible. The task transcript/rollout file still exists locally, so moving the generated task output was not the cause. No local task-store files were modified during investigation.Validated fix against current
openai/codexmain (2a30972fcb646f9d6bfed6de6606aa3f0f8c3dd2) on Windows x64.The duplicate-move bug affects both archive and the symmetric unarchive path. The minimal fix is to deduplicate
rollout_movesby canonical source afterscoped_rollout_path()and beforerename(). This preserves the selected destination/SQLite update while ensuring one physical rollout is moved only once.I added two real Windows regression tests:
archive_thread_deduplicates_windows_verbatim_sqlite_rollout_pathunarchive_thread_deduplicates_windows_verbatim_sqlite_rollout_pathEach test creates one physical rollout, stores its
std::fs::canonicalize()result (a\\?\verbatim path on Windows) in SQLite, and letsRolloutReferenceIndex::scan()discover the normal path spelling.Red/green evidence:
failed to archive/unarchive thread: ... (os error 2).just test -p codex-thread-store: 224 passed, 0 skipped.just fix -p codex-thread-store: completed cleanly.git diff --check: clean.uvanddotslashformatters.The repository's CONTRIBUTING guide says external PRs are not accepted, so I am posting the complete tested patch here for a maintainer to apply:
I can independently reproduce this issue on Windows Codex Desktop using the shared app-server.
After the recent update, multiple local conversations cannot be archived. The conversations remain visible in the active list with a
notLoadedstatus, while reading them still works normally.Sending
thread/archivethrough the shared app-server consistently fails with:failed to archive session: thread-store internal error:
failed to archive thread: The system cannot find the specified file. (os error 2)
The archived-conversation listing itself remains functional, and other conversations can still appear there. No local database or session files were modified during testing.
Environment:
This appears to match the Windows archive failure described in this issue.
Independent reproduction with an additional user-impact symptom.
Environment:
Observed:
Additional impact not limited to the archive action:
No rollout, database, or project files were modified during this reproduction. This matches the normal-path/verbatim-path duplicate-move diagnosis already documented in this issue, and suggests the failed archive rollback also leaves the in-process task state unusable until restart.
Additional multi-thread reproduction: runtime teardown occurs before archive durability is known
Environment:
26.814.5517.0client_versionobserved in the archive logs:26.814.41957I reproduced the same archive failure on three unrelated completed local tasks. The new evidence concerns transaction ordering and retry behavior.
For two affected tasks:
Shutdown.had_active_turn=false.thread not found.A third task provided stronger cascade/hang evidence:
Shutdown, agent-loop exit, and listener teardown for the parent and all three children.This suggests that archive mutates live runtime state before all durable move/index prerequisites have succeeded. A failure can therefore leave a readable-but-nonmutable ghost task, and a hung archive can block a subsequent archive request.
Expected behavior:
Evidence boundary: diagnostics were read-only. No task store, JSONL, SQLite, worktree, or Desktop state was edited. The exact missing internal object was not exposed by the current error text, so this reproduction does not independently assert the path-normalization root cause.
Additional finding on the desktop packaging side (release-pipeline issue):
Environment:
client_version26.818.21641; archiving still fails with the same UI error on this build.Key evidence:
app/resources/codex.exe(Rust binary, no version resource) contains the embedded version string0.148.0-alpha.21, published 2026-08-17, i.e. before the dedup fix (#39256) was merged on 2026-08-18.rollout_movesby canonical source + regression tests) is present onmainand inrust-v0.149.0-alpha.4, but NOT inrust-v0.149.0-alpha.1/2/3,rust-v0.148.0, or the0.148.0-alpha.2xtags.Suggestion for the release pipeline: verify the desktop MSIX bundles an app-server binary that includes the dedup fix before shipping. Users who updated to 26.818 still hit the bug, and there is no way to tell from the MSIX version number alone.
No private paths, credentials, or conversation contents are included.
Additional causal isolation from Codex Desktop on Windows.
Environment
26.814.5517.00.148.0-alpha.15Isolated protocol experiment
I ran the signed bundled app-server against disposable, isolated state stores. No model turn was started and no real conversation data was used.
A 2x2 creation matrix varied both
CODEX_HOMEand the requested projectcwdbetween ordinary and Windows verbatim path spellings:In all four cases:
thread/startplusthread/name/setpersisted an ordinary, non-verbatimrollout_path;cwdwas canonicalized to a verbatim path, but this did not affect archival;I then used a fresh isolated thread and inspected the same SQLite row after each app-server operation:
A same-environment control thread that was archived directly, without
thread/resume, succeeded:archived=1;archived_sessions;This establishes that, in this build, the verbatim selected rollout path is not introduced by initial thread creation, AI-vs-user creation, the project cwd spelling, or the CODEX_HOME spelling. The direct upstream writer is
thread/resume.This complements the duplicate canonical-source move analysis already documented above. A regression test should cover the complete lifecycle: create with an ordinary stored rollout path, resume by thread ID, verify the persisted path representation, then archive exactly once.
No thread IDs, user paths, credentials, logs, databases, rollout contents, or project contents are included.
Current-build reproduction: failed archive rolls back the file move but not the live thread teardown
I reproduced this on the current Windows Desktop package and found a second failure boundary after the duplicate-move / verbatim-path archive error already documented here.
Environment
26.818.2441.0(Windows x64)26.818.216410.148.0gpt-5.6-sol,xhighrollout_pathused the Windows verbatim form\\?\C:\...Sanitized sequence
The archive action was attempted a second time from the same sidebar menu and failed identically.
State after failure
7,571items andparse errors: 0.archived=0and the active rollout path.This is consistent with the canonical-source duplicate move described above: the filesystem portion restores the earlier move when the second rename fails. However, the runtime teardown is not restored with it. The failed archive therefore converts an otherwise readable thread into a visible-but-dead thread until recovery/restart.
Suggested invariant
thread/archiveshould be transactional across both storage and runtime state:thread/settings/updateorturn/start—it should explicitly resume/reconcile it first.A regression test could force the duplicate-source second-rename failure, then assert that the same thread can still accept a settings update or a new turn after
thread/archivereturns the error.No private paths, thread/turn IDs, task titles, prompts, account data, conversation contents, screenshots, or raw logs are included.
Additional population-level and post-update evidence from Windows Codex Desktop:
Environment
OpenAI.Codex 26.818.2441.026.818.21641Impact and recurrence
A read-only census taken before repair found:
threads.rollout_pathbegan with the Windows verbatim prefix\\?\Across ten independent SQLite backup snapshots for that same 22-thread cohort:
This shows that the prefix is not only legacy data; it is re-persisted later in the thread lifecycle.
Failure after the 26.818 update
Two distinct completed local threads failed on 2026-08-20 at
10:57:30Zand11:47:57Z. Both requests returned:The source rollouts existed and remained readable.
A narrow startup guard was already active: it only normalizes the exact startup session's one
rollout_pathrow after creating an independent SQLite backup, verifies the result, and never archives automatically. The later failures still occurred, confirming that startup-only normalization cannot prevent the running app from reintroducing the verbatim path (consistent with thethread/resumeevidence already reported here).For each affected thread, changing only
rollout_pathfrom the verbatim form to the equivalent ordinary path, then using the official archive command, succeeded. Post-archive verification found the database row and archived rollout consistent, with no missing or mismatched files.This is additional evidence that Desktop
26.818.2441.0still ships an archive core without the deduplication/path-normalization fix. Please include the existing Windows archive/unarchive regression fix in the bundled Desktop app-server and test the full lifecycle:No thread IDs, user paths, account data, rollout contents, databases, or raw logs are included.
Independent confirmation from another Windows Codex Desktop installation.
Environment
OpenAI.Codex 26.818.2441.026.818.216410.148.0-alpha.15Population-level evidence
A read-only census found:
threads.rollout_pathused the Windows verbatim prefix\\?\C:\...pathAttempting to archive one affected completed thread returned:
After creating a consistent SQLite backup and changing only that row's
rollout_pathfrom the verbatim spelling to the equivalent ordinary spelling, the official archive operation succeeded immediately. The thread appeared in the archived list and disappeared from the recent list.A guarded bulk normalization then changed all 42 affected rows, with zero missing files afterward. During the still-active diagnostic thread, the app persisted that active row back to the
\\?\C:\...form, leaving one affected row again. This independently confirms that normalization is only a workaround: the running Desktop lifecycle can reintroduce the incompatible path representation, so newly created/resumed threads are not guaranteed to remain archivable.Please fix the archive candidate deduplication/path identity handling and ensure
thread/resumedoes not persist a path spelling thatthread/archivecannot process.No user name, thread IDs, task titles, conversation content, private paths, databases, or raw logs are included.
Confirmed fixed in the shipped desktop build.
Environment:
0.149.0(verified via the embedded version string inapp/resources/codex.exe).Verification:
os error 2on 26.814.x and on 26.818.2441.0).rust-v0.149.0tag —archive_thread.rsdeduplicatesrollout_movesby canonical source, and the regression testarchive_thread_deduplicates_rollout_pathsis included.0.148.0-alpha.21(pre-fix), which is why the earlier 26.818 update did not resolve the issue; 26.818.3698.0 bundles0.149.0(post-fix).No private paths or conversation contents are included. Thanks for the fix — this issue can likely be closed.