[WSL] Resumed sessions get a lowercased /mnt cwd from the state DB, breaking project trust and approval matching

Open 💬 1 comment Opened Aug 14, 2026 by scmmm

What version of Codex CLI is running?

0.147.0

What subscription do you have?

plus

Which model were you using?

_No response_

What platform is your computer?

_No response_

What terminal emulator and version are you using (if applicable)?

_No response_

Codex doctor report

What issue are you seeing?

On WSL2, when a project lives on a Windows drive mounted under /mnt/<drive> and the path contains mixed-case components, every resumed session restores an all-lowercased cwd. For example, the original cwd /mnt/m/2026/TesT becomes /mnt/m/2026/test after resume.

This is not just cosmetic — it breaks permission management:

  1. Project trust is lost. ~/.codex/config.toml stores trusted projects keyed by the original-case path ([projects."/mnt/m/2026/TesT"] trust_level = "trusted"), but the resumed session's cwd is lowercase test, so the trust lookup misses. The trust screen is shown again and project-local config / hooks / exec policies are treated as untrusted.
  2. Commands require approval every time. Path-based access checks compare against workspace roots / recorded paths that still carry the original casing, so they no longer match. Commands that previously ran without prompting now ask for approval — even though on the drvfs mount test and TesT resolve to the same directory.

<img width="1211" height="843" alt="Image" src="https://github.com/user-attachments/assets/4f7f0a2b-187b-403f-b705-9a6951fc8e9d" />
<img width="1106" height="843" alt="Image" src="https://github.com/user-attachments/assets/2c198809-c690-4998-86e0-cfbae22df63b" />

What steps can reproduce the bug?

  1. On WSL2 create a mixed-case dir on a Windows drive: mkdir -p /mnt/<drive>/path/TesT && cd /mnt/<drive>/path/TesT
  2. Start codex, send any message, and trust the project when prompted (writes the original-case key into config.toml). Let a command get approved/run.
  3. /new in the same window.
  4. Exit, then resume the session (codex resume or /resume).

What is the expected behavior?

_No response_

Additional information

I successfully fixed this issue at https://github.com/scmmm/codex/tree/fix/wsl-resume-cwd-case.

View original on GitHub ↗

1 Comment

jdcodes1 · 11 days ago

Traced this on main @ 1f41cc5d92 — the lowercasing and the trust break are two halves of one asymmetry: a comparison normalization is being persisted as an identity value, while the stored trust keys never get the same normalization.

Where the lowercase comes from. normalize_for_path_comparison canonicalizes and then, on WSL, ASCII-lowercases any /mnt/<drive>/... path (codex-rs/utils/path-utils/src/lib.rs#L148-L211normalize_for_wsl_with_flaglower_ascii_path). That's a reasonable comparison key for a case-insensitive drvfs mount. The problem is that the thread stores persist it as the thread's actual cwd:

So resume restores /mnt/m/2026/test as the working cwd — the comparison key has leaked into identity.

Why trust lookup then misses, precisely. The project-trust lookup builds candidate keys from the query path — the raw string plus its comparison-normalized form — but the stored [projects."..."] keys are only lowercased under cfg!(windows), which is false in a WSL (Linux) binary:

https://github.com/openai/codex/blob/1f41cc5d92722748e45cae9cecc6d883a4e7cbb1/codex-rs/config/src/config_toml.rs#L841-L862

Walk it through: in the original session, cwd /mnt/m/2026/TesT produces keys ["/mnt/m/2026/test", "/mnt/m/2026/TesT"], and the second one hits the stored key → trusted. After resume the cwd is already lowercased, so the candidate set collapses to ["/mnt/m/2026/test"] — and the stored TesT key is never normalized on the map side → miss → trust screen again. The same one-sided normalization explains the approval/workspace-root mismatches.

Fix shape. Two independent corrections, both worth doing:

  1. Stop persisting the comparison form as identity — store the case-preserved canonical cwd in the state DB / thread metadata and apply normalize_for_path_comparison only at compare time (or in a dedicated lookup column). This fixes resume cwd, approval matching, and the cosmetic path in one move. @scmmm's branch (fix/wsl-resume-cwd-case, touching rollout/src/recorder.rs, thread-store/src/local/read_thread.rs, tui/src/session_resume.rs) looks like this shape — preserving the original casing through record/read/resume.
  2. Make trust lookup symmetricnormalize_project_lookup_key should apply the same WSL-aware comparison normalization to the stored keys that normalized_project_lookup_keys applies to the query (today it lowercases only under cfg!(windows)). With both sides normalized, either casing of the same drvfs directory resolves to the same trust entry, which also retroactively repairs configs that already contain mixed-case keys — fix (1) alone leaves previously-lowercased DB rows failing against mixed-case trust keys.

A regression test can stay entirely on Linux CI by driving the _with_flag variants (normalize_for_wsl_with_flag(path, /*is_wsl*/ true)) — no WSL environment needed: assert that a /mnt/m/…/TesT cwd round-trips through persist→resume with case preserved, and that trust lookup matches in both directions with a mixed-case stored key.