openai / openai/codex

[Windows][WSL] threads.cwd stores WSL paths joined onto the current drive (/mnt/e/test → C:\mnt\e\test), breaking thread resume

Open
#42,292 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

app bug session windows-os
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

What version of Codex are you using?

Codex App on Windows with runCodexInWindowsSubsystemForLinux = true (Agent Environment = WSL).

Affected threads rows in state_5.sqlite span many CLI versions:

0.132.0, 0.137.0, 0.142.0, 0.142.2, 0.142.4, 0.142.5,
0.144.0-alpha.4, 0.147.0-alpha.6.6

Native Windows CLI used for the failing resume: v0.152.1.
Linux CLI used for the diagnostics below: codex-cli 0.145.0 / Codex Doctor v0.145.0 · linux-x86_64.

What subscription do you have?

ChatGPT-authenticated Codex account. Exact tier is not part of local diagnostics.

What platform is your computer?

  • Host: Windows, CODEX_HOME = C:\Users\<user>\.codex
  • Agent environment: WSL2, Ubuntu 22.04.5 LTS, kernel 6.18.33.2-microsoft-standard-WSL2
  • In WSL mode the same CODEX_HOME is reached over DrvFs as /mnt/c/Users/<user>/.codex

What issue are you seeing?

When Codex runs in WSL mode, the POSIX cwd of a thread is written into the
threads table of state_5.sqlite without any WSL→Windows path mapping.
Instead of mapping /mnt/<drive>/<path> to <drive>:\<path>, the value is
treated as a drive-relative absolute path and joined onto the current drive,
producing paths that do not and cannot exist:

rollout JSONL session_meta.payload.cwd (correct) threads.cwd in state_5.sqlite (corrupted)
/mnt/e/test C:\mnt\e\test
/mnt/c/Users/<user> \\?\C:\mnt\c\Users\<user>
/mnt/c/Users \\?\C:\mnt\c\Users
/mnt/c/Users/<user>/.codex \\?\C:\mnt\c\Users\<user>\.codex

Note the second row: /mnt/c/Users/<user> already points back at the Windows
C: drive, yet it is re-joined onto C: and gains a spurious mnt\c segment.

The transcript on disk is fine — only the SQLite thread index is wrong. Since
resume reads the index rather than the JSONL, the bad value is what surfaces:
opening such a thread from the native Windows CLI shows

directory:   C:\mnt\e\test

and the resume fails before the session is usable.

Scale on one machine
threads rows total                          : 575
cwd correctly kept as POSIX (/mnt/...)      : 531
cwd corrupted into <drive>:\mnt\...         :  29
cwd natively Windows                        :  15

The 29 corrupted rows come from both source = 'cli' and source = 'vscode'
and from 8 distinct CLI versions, so this is not a one-off regression of a
single build.

Control experiment: same thread, same config, only the resume environment differs

The corrupted cwd is not cosmetic. Resuming the same thread behaves
differently depending on where resume runs, with everything else held constant
(same CODEX_HOME, same config.toml, same account, same provider):

resume from result
Codex App with the WSL agent environment works, thread resumes normally
native Windows PowerShell, codex resume <rollout.jsonl> fails

The native-Windows failure:

Error: Failed to resume session from \\?\C:\Users\<user>\.codex\sessions\<...>.jsonl:
thread/resume failed during TUI bootstrap: thread/resume failed:
failed to load configuration: Model provider `OpenAI` not found (code -32600)

The TUI header shows the corrupted directory before it fails:

model:       loading   /model to change
directory:   C:\mnt\e\test
permissions: YOLO mode

The provider itself is healthy. [model_providers.OpenAI] is present in
config.toml; codex doctor reports config.toml parse ok and
default model provider OpenAI; and the same provider resumes the same thread
without trouble from the WSL side. Two things were ruled out separately by
constructing minimal CODEX_HOME fixtures:

  • a provider defined without base_url still loads fine (✓ config loaded),
    so the missing base_url on this machine is not the trigger;
  • only a wholly absent provider definition reproduces a config-load failure.

So Model provider not found looks like a downstream symptom of a cwd that
cannot exist on the target platform, rather than a configuration problem in its
own right.

What steps can reproduce the bug?

  1. In Codex App on Windows, set the Agent Environment to WSL
    (desktop.runCodexInWindowsSubsystemForLinux = true).
  2. Start a thread whose cwd is a WSL-visible Windows drive path, e.g. /mnt/e/test.
  3. Inspect the persisted rollout and the index for the same thread id:
# transcript — correct
rg -n '"cwd"' ~/.codex/sessions/<yyyy>/<mm>/<dd>/rollout-<...>.jsonl | head -1

# index — corrupted
sqlite3 ~/.codex/state_5.sqlite \
  "select id, source, cli_version, cwd from threads where id='<thread-id>';"

Observed: JSONL says /mnt/e/test, the threads row says C:\mnt\e\test.

  1. Resume that same thread from a native Windows shell:
codex resume C:\Users\<user>\.codex\sessions\<yyyy>\<mm>\<dd>\rollout-<...>.jsonl

It fails with failed to load configuration: Model provider ... not found,
and the TUI header shows directory: C:\mnt\e\test. Resuming the same
thread from the WSL side succeeds.

A bulk check across the whole table:

select cwd, count(*), group_concat(distinct cli_version)
from threads
where cwd glob '[A-Za-z]:\mnt\*' or cwd glob '\\?\[A-Za-z]:\mnt\*'
group by cwd order by 2 desc;

What is the expected behavior?

  • When Codex runs under WSL, a POSIX cwd persisted to threads.cwd should
    either be stored verbatim as POSIX, or be mapped correctly
    (/mnt/<drive>/<path><drive>:\<path>) — never joined onto the current
    drive to form <drive>:\mnt\<drive>\....
  • resume should reconcile threads.cwd against
    session_meta.payload.cwd in the rollout JSONL when the two disagree, and
    prefer the transcript, which is written correctly.
  • codex doctor should be able to detect and repair thread rows whose cwd
    cannot exist on the current platform. There is currently no supported
    reindex/repair path (related: #31433).
  • If a thread genuinely cannot be resumed from the other environment, the error
    should name the real cause (unusable/unmapped cwd) instead of surfacing as
    failed to load configuration: Model provider ... not found, which sends users
    to edit a config.toml that is already correct.

Additional information

Secondary finding: SQLite is not reliably accessible from the WSL side

In WSL mode the CLI reaches CODEX_HOME over DrvFs (/mnt/c/...). Every
SQLite database there fails to open from the Linux side:

state DB       .../state_5.sqlite  · integrity error: (code: 4618) disk I/O error
log DB         .../logs_2.sqlite   · integrity error: (code: 4618) disk I/O error
goals DB       .../goals_1.sqlite  · integrity error: (code: 4618) disk I/O error
memories DB    .../memories_1.sqlite · integrity error: (code: 4618) disk I/O error
thread history DB integrity error: (code: 4618) disk I/O error
rollout DB read error: (code: 4618) disk I/O error

4618 is SQLITE_IOERR_SHMOPEN — DrvFs does not support the shared-memory
mapping WAL mode requires.

The databases themselves are not damaged. Copying the same file to a native
ext4 path and re-running the check passes:

location PRAGMA integrity_check
in place on /mnt/c/... (DrvFs) disk I/O error
byte-identical copy on ext4 (/tmp) ok

So in WSL mode, index writes to state_5.sqlite are performed against a store
the Linux process cannot reliably read or write. This is a plausible contributing
factor to the stale/incorrect index rows above, and it makes codex doctor report
a hard state failure that no local repair can clear while the app stays in WSL mode.

Related issues (overlapping symptoms, different failure points):

  • #29083 — resume fails when state_5.sqlite has a blank model_provider
    while the JSONL has a valid one. Same class of "index disagrees with transcript",
    different column.
  • #18506 — Windows Codex app + WSL: UNC cwd breaks terminal, Windows config leaks into WSL.
  • #29639 — WSL workspace fails due to unmapped sandboxCwd.
  • #41463 — [Windows + WSL] AbsolutePathBuf deserialized without a base path.
  • #31433 — rollout files left unindexed, no supported reindex repair.
Suggested fix direction

The join looks like it goes through a "resolve against a base directory" path
that receives a POSIX absolute path but no WSL-aware base. Either the value
should never be resolved on the Windows side, or the resolver needs a
/mnt/<drive><drive>:\ rule before joining. A one-time migration that
rewrites existing <drive>:\mnt\<drive>\... rows from the corresponding rollout
session_meta.payload.cwd would clear the accumulated bad rows.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing the thread index and resume paths that read state_5.sqlite, then compare their cwd handling with rollout JSONL session_meta.payload.cwd. Reproduce the /mnt/e/test case and inspect the existing SQL diagnostics. Done means WSL paths are not corrupted in threads.cwd, resume handles mismatches predictably, and failures identify an unusable cwd rather than masking it as a provider error.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust, sqlite
Domain
cli, databases, operating-systems
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.