Skip to content

[Windows] Archiving fails with os error 2 when rollout paths use the \\?\ prefix; external path normalization is ineffective #39209

Closed

Description

@RuriLothlorien

Summary

On Windows, Codex Desktop cannot archive local conversations when the stored rollout path uses the Windows extended-length prefix \\?\. The archive request fails with a thread-store os error 2 even though the rollout file exists on disk.

Environment

  • Product: Codex Desktop (Microsoft Store / WindowsApps install)
  • App package: OpenAI.Codex_26.814.5167.0_x64__2p2nqsd0c76g0
  • Core (app-server): codex 26.814.41407
  • OS: Windows 11 x64, NTFS
  • System locale: zh-CN (error text appears as 系统找不到指定的文件)
  • Threads: local project threads created through the normal desktop workflow

Actual behavior

  • Clicking Archive shuts down the active thread instance, then returns:
    failed to archive session: thread-store internal error: failed to archive thread: The system cannot find the file specified. (os error 2)
  • The conversation stays visible and state_5.sqlite.threads.archived remains 0.
  • The referenced rollout .jsonl exists on disk, and the archived_sessions directory exists.

Root-cause evidence

  • state_5.sqlite (threads.rollout_path) contains a mix of path forms:
    • Failing unarchived threads: \\?\C:\Users\<user>\.codex\sessions\...
    • Previously archived threads: plain C:\Users\<user>\.codex\sessions\... (these archive successfully)
  • The extended form is actively re-persisted by the running app, not a one-time corruption:
    • An external daemon strips \\?\ from state_5.sqlite every ~3 s.
    • The running app writes the extended form back within ~16 s for the active/resumed thread.
    • Logs show: codex_rollout::recorder: Resuming rollout from "\\?\C:\Users\<user>\.codex\sessions\...jsonl".
  • OS-level file moves are not the problem: os.rename succeeds for all four combinations (\\?\→plain, plain→plain, \\?\→\\?\, plain→\\?\) on the same files. The failure is in Codex's path comparison/construction logic, not NTFS.
  • Threads created during this app run are stored with the extended form; older threads keep plain paths until they are resumed, after which the extended form returns.

Reproduction

  1. Windows Codex Desktop 26.814.5167.0, local project with existing conversations.
  2. Open a conversation whose state_5.sqlite rollout_path uses \\?\C:\....
  3. Archive it from the conversation menu.
  4. Observe os error 2; the thread remains unarchived.

Attempted workarounds and their effectiveness

  1. External daemon rewriting state_5.sqlite to strip \\?\ from rollout_path/cwd every ~3 s.
    • Effectiveness: not effective while the app is running. The app re-persists the extended form within ~16 s for active/resumed threads. It only prevents the DB from accumulating prefixed rows.
  2. Fully quit Codex → normalize the DB while stopped → restart → archive before opening/resuming the thread.
    • Effectiveness: temporarily works for threads that are already affected (their rows are plain at startup and they archive successfully), but fails again after the thread is resumed, because the extended path returns. Not a durable fix.
  3. Keeping threads closed and archiving immediately after a clean restart.
    • Effectiveness: partial; cannot help the currently active thread, and any thread resumed afterwards regresses.

Expected behavior

Archiving should either normalize Windows extended-length paths before the move, or the thread store should stop persisting \\?\-prefixed paths in the first place. Previously archived threads with plain paths do not exhibit this failure, so normalizing at read/archive time should be sufficient.

Related issues

Activity

  1. added
    bugSomething isn't working
    appIssues related to the Codex desktop app
    windows-osIssues related to Codex on Windows systems
    sessionIssues involving session (thread) management, resuming, forking, naming, archiving
    on Aug 18, 2026
  2. github-actions commented on Aug 18, 2026

    @github-actions
    Contributor
  3. RuriLothlorien commented on Aug 18, 2026

    @RuriLothlorien
    Author

    This issue is intentionally kept as a complementary, consolidated report: it focuses on the empirical record of the attempted workarounds and why they are ineffective, rather than restating the original reproduction.

    The key additional data points beyond #39150 / #39130 are:

    • The \\?\ prefix is actively re-persisted by the running app: stripping it from state_5.sqlite every ~3 s does not stick; the app writes the extended form back within ~16 s for the active/resumed thread.
    • OS-level rename works for every combination of extended/plain path forms, which narrows the bug down to Codex's path handling rather than NTFS.
    • The only currently working procedure (quit app → normalize DB → restart → archive before resuming the thread) is temporary and regresses as soon as the thread is resumed.

    Happy for maintainers to close this as a duplicate if preferred; the practical value is the workaround-effectiveness record above, which is also summarized in the #39150 thread.

  4. RuriLothlorien commented on Aug 18, 2026

    @RuriLothlorien
    Author

    Tested Codex Rescue on this machine as suggested (read-only commands only). Quick feedback:

    • codex-rescue@0.1.0-alpha.6 (npm latest) fails to launch on Windows x64: platform package codex-rescue-win32-x64 is not installed, and codex-rescue-win32-x64 returns 404 on the npm registry. codex-rescue@0.1.0-alpha.6-2 launches fine.
    • doctor --all: 17 sessions scanned, 10 healthy, 7 with findings (mostly INVALID_PERSISTED_ITEM_ID, plus INTERRUPTED_INPUT_NOT_DURABLE, UNFINISHED_TOOL_CALL, INCOMPLETE_ROLLOUT).
    • Targeted doctor + workspace on the two threads that currently fail to archive with the \\?\-prefixed rollout path:
      • One reports INVALID_PERSISTED_ITEM_ID; the other is HEALTHY.
      • workspace reports Saved Working Directory: unavailable (unknown) and Workspace Association Health: UNRECORDED for both, so it does not currently surface the \\?\ extended-path boundary on these affected legacy threads.
    • The archive failure itself remains reproducible and appears to live in the thread-store path handling; Rescue's current checks did not flag it on this machine.

    Happy to share more (sanitized) output if useful.

  5. Pdz1 commented on Aug 19, 2026

    @Pdz1

    Confirmed independently on Codex Desktop 26.814.41407.

    Platform: Microsoft Windows NT 10.0.22631.0 x64.

    I observed the same behavior:

    • state_5.sqlite stored the affected rollout_path using the \?\C:... form;
    • the referenced rollout JSONL existed through both path representations;
    • archiving failed with os error 2;
    • removing only the \?\ prefix allowed an inactive thread to archive successfully;
    • for the currently active thread, Codex wrote the \?\ prefix back into SQLite almost immediately;
    • the prefixed path also returned after restarting Codex Desktop.

    I also encountered “thread not found” for an affected existing thread. No rollout JSONL files were missing.

  6. N2meLess commented on Aug 19, 2026

    @N2meLess

    Still reproducible on Codex App 26.818.11542 on Windows. I independently observed the same \\?\ rollout path followed by os error 2 when archiving. The referenced JSONL file still exists, and both os.path.exists(path) and os.path.isfile(path) return True.

  7. N2meLess commented on Aug 19, 2026

    @N2meLess

    Additional data point: I was able to archive one older thread successfully, then unarchive/resume it successfully. After that, its stored rollout_path was \?\C:..., and a second attempt to archive the same thread now fails with the same os error 2. I did not capture the DB path immediately before the first successful archive, so I can't prove the path rewrite is the sole trigger, but the failure appeared after the unarchive/resume cycle.

  8. RuriLothlorien commented on Aug 19, 2026

    @RuriLothlorien
    Author

    Consolidating the latest independent reports (thanks @Pdz1 and @N2meLess) into one picture for maintainers:

    1. Multiple independent reproductions (Windows, Codex Desktop 26.814.x): same signature everywhere — state_5.sqlite.threads.rollout_path stored as \\?\C:\..., rollout JSONL present, archive fails with os error 2. Stripping the prefix allows an inactive thread to archive successfully; the active thread gets the prefix re-persisted almost immediately; the prefix also returns after an app restart.
    2. Still reproducible on 26.818.11542 (reported by N2meLess), so this is not fixed in the newest build either.
    3. The trigger is now fairly clear: the reported sequence "archive OK → unarchive/resume → rollout_path becomes \\?\C:\... → second archive fails" matches our local observation that resume re-persists the extended form. The failure appears right after a resume/unarchive cycle, i.e. when the thread store writes the rollout path back.

    Suggested fix directions:

    • When persisting rollout_path (especially on resume/unarchive), normalize Windows extended-length paths back to plain C:\... form, or
    • Normalize \\?\ in archive_thread before the rename/comparison (e.g. in scoped_rollout_path / path-scope checks), since previously archived threads with plain paths archive fine.

    This is no longer an isolated environment quirk: the same extended-path persistence and archive failure are reproducible across at least three machines/builds.

  9. starkovalera commented on Aug 19, 2026

    @starkovalera

    Additional independent reproduction on Windows Codex Desktop:

    • Package: OpenAI.Codex_26.814.5517.0_x64
    • The issue reproduces after fully quitting and restarting Codex; restart does not clear it.
    • A fresh local recipe-manager thread is stored with a rollout_path using the Windows verbatim prefix \?\C:...
    • The rollout JSONL exists at the equivalent normal Windows path.
    • Clicking Archive returns: failed to archive session: thread-store internal error: failed to archive thread: The system cannot find the file specified. (os error 2)
    • Normalizing only rollout_path by removing the \?\ prefix, then invoking the normal Archive action immediately, succeeds and moves the rollout to archived_sessions.
    • New threads are affected too; this is not limited to migrated historical sessions.

    This confirms the path form is actively produced by the current Desktop build and that the failure is in archive path handling. No chat contents, private paths, credentials, or source files are attached.

  10. RuriLothlorien commented on Aug 19, 2026

    @RuriLothlorien
    Author

    Thanks @shleder — the new WINDOWS_ROLLOUT_PATH_IDENTITY_DIVERGENCE check is working as intended. Confirmed on this Windows machine with codex-rescue@0.1.0-alpha.6-3:

    • The code is reported for every local thread whose state_5.sqlite.threads.rollout_path is currently stored in the \\?\C:\... form — 4/4 threads here, including two that reproduce the archive os error 2 and one newly created thread.
    • Targeted doctor <rollout-file> runs surface the code directly; no raw paths, rollout content, or database files are needed for the finding.

    Two small observations for the tool:

    • Batch doctor --all did not list these threads or emit the code in my runs; only targeted doctor <rollout-file> surfaced it. If the batch enumerates sessions differently (e.g., only catalog/index-discoverable ones), that may be worth a look.
    • One affected thread also reported MALFORMED_RECORD alongside the divergence; it was actively being appended at scan time, so that reads as a live-writer tail rather than corruption.
  11. RuriLothlorien commented on Aug 20, 2026

    @RuriLothlorien
    Author

    Quick consolidation of the latest independent confirmations (thanks @Culacco and @fireking0608-spec):

    • Still present in the current Desktop package (26.814.5517.0, bundled CLI 0.148.0-alpha.15) across multiple Windows builds.
    • Full consistency checks (e.g. 132 threads / 132 rollout files, SQLite integrity ok) rule out missing files or DB corruption.
    • Migrating legacy rollouts to paginated history does not prevent recurrence — the \\?\C:\... form is re-persisted by the app after resume/use, and the archive failure returns.
    • Removing the \\?\ prefix while Codex is stopped makes the same backend archive/unarchive successfully.
    • Locally confirmed that the archive/unarchive round-trip works with a newer standalone CLI (codex-cli 0.149.0-alpha.1) that contains the archive-path fix; 0.148.0-alpha.15 still fails on verbatim paths.

    Net: the Desktop package needs (a) archive-path normalization in its bundled thread-store, and (b) to stop re-persisting \\?\C:\... rollout paths on resume.

  12. M-T-D-N commented on Aug 21, 2026

    @M-T-D-N

    Independent confirmation on Desktop 26.818.22352

    I independently reproduced the same defect on another Windows x64 installation. This adds newer-build persistence and wider-state evidence:

    • Five failed thread/archive attempts stopped after thread-listener teardown. Every affected row had threads.rollout_path in the \\?\C:\... form.
    • After validating that every target resolved to an existing file inside the Codex session root, I transactionally normalized 25 affected active rows to the ordinary drive form. Immediately after that transaction, the count of verbatim-form active rollout paths was 0.
    • A canary archive through the normal Desktop/App Server API then succeeded. The rollout moved to the archived-session directory and the database archive state matched.
    • After subsequent normal thread-list activity, 9 active rows were persisted again in the verbatim form. This confirms that one-time database normalization is only a per-target workaround; ordinary app activity recreates the precondition.
    • PRAGMA integrity_check returned ok. The wider audit found no database rows with missing rollout files, no archived-flag/location mismatches, no filename/thread-ID mismatches, and no duplicate canonical rollout paths.

    This supports the issue's existing conclusion: the source file is not missing, and inconsistent Windows path representation across persistence/list and archive stages is the likely proximate cause. The exact writer function and exact failing archive-stage operation remain inferred rather than source-traced.

    A durable fix should normalize at the threads.rollout_path persistence boundary and make archive path handling representation-independent. Regression coverage should include ordinary drive paths, verbatim drive paths, verbatim UNC paths, list/resume idempotence, and archive/unarchive round trips.

    No user paths, project names, thread/session IDs, raw logs, session contents, or database files are included.

    AI disclosure

    At the user's direction, this report transparently records that the user authorized the submission and completed the GitHub authentication step, while the investigation, analysis, drafting, and GitHub posting of this technical report were performed by an AI agent (OpenAI Codex) on the user's behalf.

  13. RuriLothlorien commented on Aug 21, 2026

    @RuriLothlorien
    Author

    Thanks @shleder — reran on codex-rescue@0.1.0-alpha.7 as requested (read-only). Results from this Windows machine:

    • npm launch: fixed — npx --yes codex-rescue@0.1.0-alpha.7 now starts cleanly on Windows x64 (no codex-rescue-win32-x64 404).
    • Targeted runs: still correct — doctor <rollout-file> surfaces WINDOWS_ROLLOUT_PATH_IDENTITY_DIVERGENCE on every thread whose state_5.sqlite.threads.rollout_path is currently in the \\?\C:\... form (3/3 tested here, including a thread created today).
    • Batch: improved but not fully fixed on this machine — doctor --all now scans more sessions (25 vs ~20), but doctor --all --json still contains 0 WINDOWS_ROLLOUT_PATH_IDENTITY_DIVERGENCE (3 THREAD_IDENTITY_CONFLICT entries appear), and the currently-prefixed threads are still not listed by thread id in the batch output.

    So the recursive enumeration change helped coverage, but on this machine the divergence code still only appears via targeted doctor <rollout-file> runs. Happy to provide more sanitized output if useful.

  14. RuriLothlorien commented on Aug 22, 2026

    @RuriLothlorien
    Author

    Fixed-version confirmation (Windows):

    • Desktop package: 26.818.5229.0; bundled/standalone CLI: codex-cli 0.149.0-alpha.4.1.
    • Successfully archived threads whose state_5.sqlite.threads.rollout_path was still in the \\?\C:\... form (including a thread that previously failed with os error 2): thread/archive returned success, the rollout moved to archived_sessions, and archived=1 with a plain path.
    • No os error 2 / archive failures in the logs after this version.
    • Note: the app still persists \\?\C:\... paths for new/active threads (14 such rows remain here), but archive now handles them correctly — i.e. archive-time path handling is representation-independent now, while the persistence-side normalization suggested earlier does not appear to be implemented yet.

    This matches the expected fix direction (handle the verbatim form at archive/read time) and resolves the practical failure on current builds.

  15. RuriLothlorien commented on Aug 24, 2026

    @RuriLothlorien
    Author

    Successfully archived threads

  16. RuriLothlorien commented on Aug 24, 2026

    @RuriLothlorien
    Author

    @shleder — I tested the newly published @shleddy/vetto@next (0.2.0-alpha.2) on this Windows machine, read-only. Results:

    Batch discovery — fixed

    • vetto rescue scan now enumerates 59 sessions, including all 12 threads on this machine whose state_5.sqlite.threads.rollout_path is stored in the \\?\C:\... form. Previously codex-rescue doctor --all (alpha.7) skipped these and only targeted doctor <file> surfaced them.
    • Date-partitioned rollouts (sessions/YYYY/MM/DD/...) are included in the batch listing.

    Divergence diagnostic — not in this npm alpha yet

    • vetto rescue diagnose <key> on two affected verbatim-prefixed threads (including the currently active one) returns health: healthy, notices: [], and does not emit WINDOWS_ROLLOUT_PATH_IDENTITY_DIVERGENCE.
    • This matches your note that the SQLite-inventory divergence check is merged into Vetto main and will land in a later npm alpha.

    Also noting the CLI rename: doctor is now rescue scan / rescue diagnose.

    Happy to rerun once a newer alpha with the inventory check ships. Thanks for moving this forward.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    appIssues related to the Codex desktop appbugSomething isn't workingsessionIssues involving session (thread) management, resuming, forking, naming, archivingwindows-osIssues related to Codex on Windows systems

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions