Codex cannot resume running thread when Windows session path differs only by \\?\ prefix

Resolved 💬 7 comments Opened May 28, 2026 by Hal9000AIML Closed Jun 2, 2026

What happened?

After a Codex conversation has been running for a while, attempting to resume the existing running thread can fail with a stale path error. The two paths in the error appear to refer to the same session JSONL file, but one is a normal Windows path and the other uses the extended-length \\?\ path prefix.

Redacted example:

cannot resume running thread <THREAD_ID> with stale path: requested `C:\Users\<USER>\.codex\sessions\YYYY\MM\DD\rollout-YYYY-MM-DDTHH-MM-SS-<THREAD_ID>.jsonl`, active `\\?\C:\Users\<USER>\.codex\sessions\YYYY\MM\DD\rollout-YYYY-MM-DDTHH-MM-SS-<THREAD_ID>.jsonl`

These paths are equivalent on Windows, but Codex appears to treat them as different and refuses to resume the thread.

Expected behavior

Codex should resume the existing running thread when the requested and active session paths differ only by Windows path normalization, such as the presence or absence of the \\?\ extended-length prefix.

The user should not need to manually copy and paste the conversation into a new session to continue.

Actual behavior

Codex blocks resume with a stale path mismatch and requires starting a new session or manually transferring context.

Impact

This breaks continuity for long-running Codex sessions. The underlying session file still exists and appears to be the same file, but the resume logic rejects it because the path strings are formatted differently.

Likely cause

The resume logic may be comparing raw path strings rather than comparing normalized/canonical Windows filesystem paths.

Suggested fix

Normalize both paths before comparing them on Windows. For example:

  • Treat C:\... and \\?\C:\... as equivalent when they resolve to the same filesystem path.
  • Canonicalize both paths through the same Windows path API before comparison.
  • Store internal session paths in one canonical format and only render user-facing paths separately.

Environment

OS: Windows
OS version observed: Microsoft Windows NT 10.0.26200.0
Shell observed: PowerShell 7.6.1
Codex session storage path: C:\Users\<USER>\.codex\sessions\...
Repository: openai/codex

Redaction note

The original username, thread ID, timestamp, and exact session file name were redacted in this report.

View original on GitHub ↗

7 Comments

Pluviobyte · 1 month ago

Root cause located: the resume guard compares rollout paths with raw PathBuf equality instead of the normalization helper the rest of the codebase already uses.

The stale-path check is in resume_running_thread, codex-rs/app-server/src/request_processors/thread_processor.rs (around L2786–L2799 on 56958f2):

let existing_thread_rollout_path = existing_thread.rollout_path();
let active_path = existing_thread_rollout_path
    .as_ref()
    .or(source_thread.rollout_path.as_ref());
if let (Some(requested_path), Some(active_path)) = (params.path.as_ref(), active_path)
    && requested_path != active_path   // <-- bytewise PathBuf comparison
{
    return Err(invalid_request(format!(
        "cannot resume running thread {existing_thread_id} with stale path: requested `{}`, active `{}`",
        ...

The two operands come from different sources and can legitimately differ in their Windows normalization form:

  • requested_path = params.path, passed straight from the client (plain C:\Users\...\rollout-….jsonl).
  • active_path = the running thread's recorded rollout_path() (the extended-length \\?\C:\Users\... form, exactly as shown in the report).

PathBuf's PartialEq is a bytewise comparison of the underlying OS string, so \\?\C:\… and C:\… are unequal even though they resolve to the same file → resume is rejected with the stale-path error.

This guard is the only path comparison in this area that doesn't normalize first. The repo already ships the right primitive — codex_utils_path::paths_match_after_normalization (codex-rs/utils/path-utils/src/lib.rs), which canonicalizes both operands (on Windows std::fs::canonicalize maps both C:\… and \\?\C:\… to the same extended-length form) and falls back to raw equality if canonicalization fails. It's used for resume/cwd matching in tui/src/session_resume.rs, tui/src/resume_picker.rs, rollout/src/recorder.rs, exec/src/lib.rs, and even in this same file at L3516 for the thread-list cwd filter — just not in this resume guard.

There's already a #[cfg(windows)] unit test covering exactly this case (utils/path-utils/src/path_utils_tests.rs, matches_windows_verbatim_paths): it asserts paths_match_after_normalization(\\?\<dir>, <dir>) is true.

Suggested fix (outline): replace the raw requested_path != active_path with !path_utils::paths_match_after_normalization(requested_path, active_path). path_utils is already imported and used in this file, so no new dependency. Equivalent paths (verbatim \\?\ prefix, and also the case-insensitive /mnt/<drive> WSL form handled by the same helper) would then resume correctly, while genuinely different paths still hit the guard via the raw-equality fallback. A regression test in app-server/tests/suite/v2/thread_resume.rs mirroring the existing stale-path test, but with a verbatim-vs-plain pair, would lock this in (on Windows).

Happy to put up a PR with this change + test if a maintainer wants to take it

vrudik · 1 month ago

I’m seeing the same issue in Codex Desktop on Windows with a heartbeat automation attached to an existing thread.

Observed error:

Could not start automation
cannot resume running thread <THREAD_ID> with stale path: requested `C:\Codex\sessions\2026\05\23\rollout-2026-05-23T15-29-26-<THREAD_ID>.jsonl`, active `\\?\C:\Codex\sessions\2026\05\23\rollout-2026-05-23T15-29-26-<THREAD_ID>.jsonl`

Context:

  • Product: Codex Desktop
  • Platform: Windows
  • Automation kind: heartbeat / thread resume automation
  • The automation is active and targeted at the same thread.
  • Recreating/updating the heartbeat with destination=thread can temporarily rebind it, but the same stale path error can recur.
  • The requested and active paths differ only by the Windows extended-length \\?\ prefix and appear to refer to the same session JSONL file.

Impact: recurring heartbeat automations become unreliable because they fail to resume the current running thread, even though the target thread/session file is the same.

Expected: the resume/stale-path check should canonicalize Windows paths or compare file identity so C:\... and \\?\C:\... are treated as equivalent when they resolve to the same file.

Shark61695 · 1 month ago

Here's my Codex generated comment when I encountered this error...

I hit this exact issue on Windows today, and the error signature matched this report exactly: the requested path was a normal C:\... path and the active path was the same file in \\?\C:\... form.

A few details from my case in case they help confirm scope:

  • Thread id: 019e7898-cccf-7b41-8215-f39dbae306c9
  • The rollout/session file existed on disk and was readable at the requested path
  • The session metadata looked normal
  • This did not appear to be data loss or a missing file; it looked like resume rejected two equivalent Windows path spellings

The concrete error was:

cannot resume running thread 019e7898-cccf-7b41-8215-f39dbae306c9 with stale path: requested 'C:\Users\Shark\.codex\sessions\2026\05\30\rollout-2026-05-30T12-15-40-019e7898-cccf-7b41-8215-f39dbae306c9.jsonl', active '\\?\C:\Users\Shark\.codex\sessions\2026\05\30\rollout-2026-05-30T12-15-40-019e7898-cccf-7b41-8215-f39dbae306c9.jsonl'

Workaround that worked for me: fully quit Codex and reopen it. After restart, I was able to resume the chat normally.

So from the user side, a full app restart seems to clear the stale running-thread state, but the underlying bug still looks exactly like the raw path comparison described above.

Hal9000AIML · 1 month ago

FYI. It is still doing this. Even with the newest update from 5 minutes ago.

linerking · 1 month ago

I am seeing the same stale-path resume failure on a Windows host, with one extra detail: it is triggered through remote access and restarting Codex only fixes it temporarily.

Environment:

  • OS: Microsoft Windows 11 Pro, 10.0.26100, 64-bit
  • Codex CLI: 0.136.0-alpha.2
  • Codex Desktop package path observed: OpenAI.Codex_26.601.2237.0_x64__2p2nqsd0c76g0
  • Surface: Codex Desktop on a Windows host, accessed remotely

Observed:

  • Remote access to a local Windows Codex project fails when resuming a running thread.
  • The error shape is:
cannot resume running thread <THREAD_ID> with stale path:
requested `C:\Users\<USER>\.codex\sessions\YYYY\MM\DD\rollout-...-<THREAD_ID>.jsonl`,
active `\\?\C:\Users\<USER>\.codex\sessions\YYYY\MM\DD\rollout-...-<THREAD_ID>.jsonl`
  • The two paths refer to the same Windows file.
  • The session JSONL exists and is readable locally.
  • Local logs showed the old thread received shutdown and the agent loop exited, but resume still rejected the equivalent path as stale.

Impact:

  • Remote access becomes unable to continue the existing running thread.
  • Restarting Codex temporarily fixes the issue.
  • The problem reappears after a while, so restart is only a short-term workaround.

This looks consistent with a raw string comparison of rollout paths before Windows path normalization/canonicalization.

andrewkangkr · 1 month ago

Additional reproduction evidence from Windows Codex Desktop 26.601.2237.0.

I hit the same stale-path class again on thread 019e8d38-69a3-76a0-96e1-ceea6b8d1be9:

cannot resume running thread 019e8d38-69a3-76a0-96e1-ceea6b8d1be9 with stale path:
requested `C:\Users\<USER>\.codex\sessions\2026\06\03\rollout-2026-06-03T20-22-25-019e8d38-69a3-76a0-96e1-ceea6b8d1be9.jsonl`,
active `\\?\C:\Users\<USER>\.codex\sessions\2026\06\03\rollout-2026-06-03T20-22-25-019e8d38-69a3-76a0-96e1-ceea6b8d1be9.jsonl`

Local evidence:

  • The rollout JSONL file exists on disk.
  • state_5.sqlite still contains the thread row.
  • In that thread row:
  • rollout_path is stored as normal C:\...
  • cwd is stored as extended path \\?\C:\...
  • session_index.jsonl contains the same thread id twice with different thread names.
  • read_thread in the app reports the thread as idle, so it is not obviously a real actively-running thread from the user's point of view.
  • The thread had to be retired and replaced with a new continuation thread because the stale-path state would not clear cleanly.

A few concrete observations that may help narrow the bug:

  1. This looks like mixed Windows path canonicalization inside the same thread state, not just between caller and callee.
  2. It is possible for the thread to appear idle in thread metadata but still be rejected as if an active path binding exists.
  3. Duplicate session_index.jsonl entries for the same thread id may be correlated with the bad resume state.
  4. Pinning / archiving / retitling the old thread is not a real fix; it only works around the broken state by moving users to a replacement thread.

Observed app/process environment:

  • Codex app package: OpenAI.Codex_26.601.2237.0_x64__2p2nqsd0c76g0
  • Multiple codex.exe app-server --listen stdio:// processes were present when the issue was observed.

What would help most:

  • Normalize equivalent Windows paths before stale-session comparison.
  • Ensure the same canonical format is used consistently for cwd, rollout_path, and in-memory active-thread bindings.
  • Provide a safe built-in repair/rebind path for a thread that is logically idle but blocked by stale active-path state.
linerking · 1 month ago

Follow-up from the same Windows host: this has recurred after a restart workaround.

Timeline:

  • 2026-06-03 19:54 local time: Codex was restarted with a local PowerShell helper; the restart log ended successfully with Desktop + app-server running.
  • 2026-06-04 local time: remote access again gets stuck loading/unable to resume the local project/thread.

Current process state still shows the Windows Codex desktop process and codex.exe app-server running, so this appears to be the remote/thread-state path issue recurring while the host-side processes remain alive, rather than a simple app-server process crash.