Windows Desktop + WSL app-server reads WSL ~/.codex instead of the Windows Codex home

Open 💬 4 comments Opened May 15, 2026 by MisterRound

What version of Codex is running?

  • Codex Desktop app package: OpenAI.Codex_26.506.3741.0_x64
  • codex-cli 0.130.0-alpha.5

What subscription do you have?

Pro

Which model were you using?

GPT-5.5

What platform is your computer?

Windows 11 Desktop app with WSL2 Ubuntu enabled. The Codex app is the Windows desktop app, but the app-server is launched through WSL.

What issue are you seeing?

In this hybrid Windows Desktop + WSL setup, Codex appears to read different config homes depending on launch context:

  • The Windows-side CLI / desktop settings UI use the Windows Codex home.
  • The WSL app-server launched by the Windows desktop app reads the WSL user's ~/.codex/config.toml.

That creates split-brain config behavior. A feature flag or setting changed in the Windows config can silently differ from what the running app-server actually uses.

This caused user-visible issues here:

  1. Deprecation warnings in the desktop UI were coming from deprecated keys in the WSL config, even though the Windows config no longer had those keys.
  2. The shell-runner regression tied to unified_exec reappeared because the WSL config still had unified_exec = true while the Windows config had already been set to false.

Steps to reproduce

  1. Use Codex Desktop on Windows in a WSL-backed workflow.
  2. Make the Windows config and WSL config intentionally differ:

Windows config:

[features]
unified_exec = false
multi_agent = true
hooks = true

WSL config:

[features]
unified_exec = true
collab = true
  1. Launch Codex Desktop and open a WSL-backed workspace.
  2. Observe desktop behavior such as:
  • config deprecation warnings
  • effective shell-runner behavior
  • feature state as seen by the WSL app-server

Expected behavior

The Windows desktop app and the app-server it launches should use one coherent config source, or there should be an explicit documented precedence model that is visible to the user.

At minimum, if the app-server is going to use WSL ~/.codex, the desktop app should surface that clearly instead of making the Windows config look authoritative.

Actual behavior

The running WSL app-server used the WSL home config, while the user-facing Windows app configuration lived in the Windows Codex home.

Observed evidence:

  • CODEX_HOME=/mnt/c/Users/.../.codex was present in the interactive shell environment.
  • The running Windows Codex process launched the WSL app-server with a command line equivalent to:
wsl.exe -d Ubuntu --cd ".../resources" -- /usr/bin/bash -lc "... exec /mnt/c/Users/.../.codex/bin/wsl/codex app-server --analytics-default-enabled"
  • That launch path did not visibly pass CODEX_HOME through to the WSL app-server process.
  • After inspecting /home/<user>/.codex/config.toml, it contained the deprecated collab = true key that matched the warnings shown in the Windows app.
  • Changing /home/<user>/.codex/config.toml immediately changed the effective feature set of the WSL app-server.

Why this is a bug

In a Windows desktop product, users reasonably expect the desktop app's config home to be the config the app actually uses.

Using the WSL user's ~/.codex/config.toml behind the scenes creates:

  • confusing deprecation warnings
  • feature flag drift
  • inconsistent shell-runner behavior
  • difficult recovery, because changing the visible Windows config may not fix the live app

Related issue

This seems related to, but distinct from, the shell-runner bug in #22185.

That issue describes the unified_exec failure mode itself. This issue is about the Windows desktop app and its WSL app-server using different config homes, which can unexpectedly reactivate that failure even after the Windows config is fixed.

Suggested fix

One of:

  1. Ensure the Windows desktop app passes the intended CODEX_HOME into the WSL app-server process.
  2. Standardize on the Windows config home for Windows Desktop sessions, even when the app-server runs inside WSL.
  3. If dual-home behavior is intentional, expose the active config path in the UI and logs so users can tell which config file is authoritative.

View original on GitHub ↗

4 Comments

MisterRound · 3 months ago

Additional data point from the same Windows Desktop + WSL setup after auditing both config homes.

Current state:

  • Windows Desktop config: /mnt/c/Users/<user>/.codex/config.toml
  • Legacy/standalone WSL config: /home/<wsl-user>/.codex/config.toml
  • Both currently have [features] unified_exec = false, which prevents the shell regression from being reintroduced by config drift.

The confusing part is that there are two competing requirements:

  1. For product stability, Windows Desktop should have one clearly authoritative home/config, currently C:\Users\<user>\.codex.
  2. For WSL performance, the WSL runtime/worktrees/cache should not live under /mnt/c/Users/<user>/.codex.

I tested a guarded WSL shell-profile override that sets CODEX_HOME=/home/<wsl-user>/.codex-app only when CODEX_INTERNAL_ORIGINATOR_OVERRIDE=Codex Desktop. It is a real workaround for the /mnt/c performance/worktree issue, but I reverted it because it makes config/home split-brain worse:

  • Desktop settings can update Windows config while the running WSL app-server reads WSL-native config.
  • Auth/plugins/skills/session DBs can diverge.
  • Users can end up unable to tell which config.toml is effective without inspecting runtime env.

Suggested fix direction: split these concepts explicitly instead of overloading CODEX_HOME:

  • Windows Desktop home/state/config path
  • WSL runtime cache/tmp/worktree root
  • Effective config path shown in UI/logs/status

That would avoid both the Windows-side /mnt/c performance issue and the current hidden config precedence ambiguity.

pussikill · 2 months ago

I want to avoid fragmentation here, so I’m treating this as a follow-up to #22759 and linking the closely related reports that appear to cover the same failure modes:

  • #13762
  • #18506
  • #26904
  • #24101
  • #24268
  • #25317
  • #25745
  • #25747
  • #26149
  • #26766
  • #25715
  • #16970

What to check in compare

Please check the working diff here:
https://github.com/openai/codex/compare/main...pussikill:codex/wsl-desktop-runtime-cache

The changes are scoped to:
1) separating persistent windows auth/state home from WSL runtime cache root,
2) moving plugin/skills runtime-heavy caches to ~/.codex/runtime/wsl/<distro> while keeping auth/session/config in Windows-backed home,
3) making arg0 temp cleanup safe for live runs,
4) skipping unsupported ACL refresh for \\wsl.localhost-style UNC paths,
5) adding an explicit warning + opt-in flag (CODEX_DESKTOP_WSL_NATIVE_CODEX_HOME) for users who explicitly want native WSL .codex.

Repro and measurement steps

  1. Build local app-server (already done in this branch): cargo build -p codex-app-server from codex/codex-rs.
  2. Launch Desktop with local WSL CLI override (example script used: launch-codex-desktop-local-wsl.sh --stop-existing) where:
  • CODEX_CLI_PATH is set to UNC path \\wsl.localhost\<distro>\home\...\codex-rs\target\debug\codex
  • CODEX_APP_SERVER_FORCE_CLI=1
  1. Confirm desktop-launched app-server process is actually from WSL debug CLI and env uses:

CODEX_HOME=/mnt/c/Users/.../.codex, CODEX_INTERNAL_ORIGINATOR_OVERRIDE=Codex Desktop, WSL_DISTRO_NAME=<distro>.

  1. Run the benchmark harness we used in this repo root:
  • python3 bench-wsl-desktop-app-server.py --isolated --clean --iterations 10
  • and the same command with store bundle replacement to compare cold/warm timing and cache placement.
  1. Attach produced bench-results/* JSON/CSV in the PR/check run.

Current numbers

  • initialize: 414ms -> 286ms (cold), 427ms -> 233ms (warm)
  • skills/list: 691ms -> 99ms (cold), 712ms -> 95ms (warm)
  • skills/list:forceReload: 715ms -> 101ms (cold), 718ms -> 95ms (warm)

I’d request triage focus on #22759 + these related reports together so we avoid duplicate fixes across home selection, ACL paths, and plugin-cache latency.

pussikill · 2 months ago

I ran a second benchmark in a hot-path scenario (same codepath, no clean reset, --isolated only) to validate the production-like working behavior after warm cache is in place.

Run: python3 bench-wsl-desktop-app-server.py --iterations 8 --isolated
Output dir: bench-results/20260608-134520

Hot-path median (8 runs):

  • initialize: store 400.639ms -> patched 216.166ms
  • skills/list: store 630.179ms -> patched 95.272ms
  • skills/list:forceReload: store 668.978ms -> patched 95.043ms
  • plugin/list: store 9.463ms -> patched 8.280ms

--isolated env remained separate and stable, and the patched path kept runtime data in ~/.codex/runtime/wsl/Ubuntu while leaving Windows HOME clean.

So this is not only a cold-start gain, but also a sustained improvement in real/reused-session flow.

pussikill · 2 months ago

I checked the non-isolated run more carefully to avoid comparing unrelated state.

The whole Windows CODEX_HOME size is not the right primary metric because it also contains persistent auth/session/config/log state. The comparable footprint is the plugin temp runtime tree used by plugin/skill discovery.

Comparable plugin temp tree:

  • store path: /mnt/c/Users/xdev/.codex/.tmp/plugins
  • 4,909 files
  • 2,564 dirs
  • 59,447,140 bytes
  • patched path: /home/xdev/.codex/runtime/wsl/Ubuntu/.tmp/plugins
  • 4,908 files
  • 2,562 dirs
  • 58,713,015 bytes

The remaining differences are mostly git metadata/refs, so this is effectively the same runtime plugin tree, just served from different filesystems.

The RPC result size was also the same:

  • config/read: 3 layers
  • plugin/list: 2 marketplaces
  • skills/list: 1 skill

Non-isolated single-run timing on the real local environment:

  • initialize: store 547.8ms -> patched 219.9ms
  • plugin/list: store 1125.0ms -> patched 479.4ms
  • skills/list: store 28816.7ms -> patched 374.0ms
  • skills/list:forceReload: store 19196.9ms -> patched 602.2ms

So the important datapoint is not “different returned data” or “different cache size”. The returned data is the same and the comparable plugin temp tree is nearly identical. The difference is that the store bundle walks that runtime tree through /mnt/c, while the patched build keeps persistent Desktop state on Windows but serves heavy runtime plugin/skill cache from native WSL storage.

This is a single-run non-isolated sanity check, not the primary statistical benchmark. I would use it mainly as confirmation that the same bottleneck reproduces in the real user environment, while the isolated multi-run benchmarks remain the cleaner apples-to-apples timing data.