[Windows][WSL] threads.cwd stores WSL paths joined onto the current drive (/mnt/e/test → C:\mnt\e\test), breaking thread resume
Nobody has claimed this yet.
- 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_HOMEis 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_urlstill loads fine (✓ config loaded),
so the missingbase_urlon 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?
- In Codex App on Windows, set the Agent Environment to WSL
(desktop.runCodexInWindowsSubsystemForLinux = true). - Start a thread whose cwd is a WSL-visible Windows drive path, e.g.
/mnt/e/test. - 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.
- 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.cwdshould
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>\.... resumeshould reconcilethreads.cwdagainst
session_meta.payload.cwdin the rollout JSONL when the two disagree, and
prefer the transcript, which is written correctly.codex doctorshould be able to detect and repair thread rows whosecwd
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/unmappedcwd) instead of surfacing as
failed to load configuration: Model provider ... not found, which sends users
to edit aconfig.tomlthat 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.sqlitehas a blankmodel_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
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 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