codex-computer-use-wsl-sandboxcwd.md — 11 min read

2026-07-0911 min readManuel ParraInvestigation

Fix Codex’s “sandboxCwd is not a local file URI” on WSL

A verified repair for Codex’s “sandboxCwd is not a local file URI” error on WSL, followed by CODEX_CLI_PATH and Windows plugin import fixes.

  • Codex
  • WSL
  • Windows
  • Computer Use
  • Chrome
  • MCP
  • Rust
On this page · 11 sections
  1. 01The setup
  2. 02Failure: request validation rejected sandboxCwd
  3. 03What I ruled out
  4. 04One directory, two local file URIs
  5. 05The patch: map sandboxCwd only for Windows STDIO servers under WSL
  6. 06Version-pinned build and tests
  7. 07Fresh-process verification exposed two more boundaries
  8. 08Checks after the build
  9. 09What I would check first next time
  10. 10Status and caveat
  11. 11Credits

Codex Desktop ran its app server inside WSL. Chrome and Computer Use used Windows plugin runtimes. Every request failed before its JavaScript ran because Codex sent a Linux file URI to a Windows MCP server in the sandbox working-directory field, sandboxCwd.

The first fix was to translate the URI for the process that received it: /mnt/c/... to file:///C:/..., and paths inside the WSL filesystem to file://wsl.localhost/<distro>/.... I applied a public patch to the exact Codex release used by the desktop app.

A fresh process exposed two more WSL-to-Windows boundaries. CODEX_CLI_PATH had to select a Windows-native sandbox helper. Plugin clients also had to load through Windows-valid file URLs.

This note follows the error, the checks that ruled out other causes, the version-pinned build, the fresh-process repairs, and the separate Chrome recovery and browser check.

The setup

Host application

Codex Desktop on Windows

Windows
Codex app server and shell

WSL2

WSL
Workspace

opened through WSL

WSL
Windows integrations

Chrome and Computer Use plugins

Windows
Tool transport

a Windows node_repl.exe registered as a local MCP server over standard input/output (STDIO)

Windows

Codex sent each request from Linux, and the local MCP server received it on Windows. Both systems could reach the workspace, but the file URI had to use the receiving operating system's path convention.

Failure: request validation rejected sandboxCwd

The smallest test asked the MCP server to evaluate a trivial JavaScript expression and return its working directory. It failed before the expression ran:

Error

Mcp error: -32602: js: codex/sandbox-state-meta:
sandboxCwd is not a local file URI: file:///mnt/c/.../project

Where it fails

sandboxCwd is a field in MCP tool-call _meta under codex/sandbox-state-meta. Codex sends it when the server advertises the sandbox-state capability. In this call, the Windows MCP server rejected the Linux-local URI during request validation. Browser selectors, Chrome state, and Windows input handling were downstream of that failure and had not run.

Fixes, in the order they were found

  1. Map sandboxCwd only for Windows STDIO servers under WSL: /mnt/c/... becomes file:///C:/.... Build the exact Codex release with the patch.

    The patch

  2. Point CODEX_CLI_PATH at a Windows-native Codex executable, so the Windows sandbox helper can start.

    The Windows sandbox helper tried to execute a Linux binary

  3. Load plugin clients through Windows-valid file URLs: file:///C:/... instead of /mnt/c/....

    Dynamic imports needed Windows-valid module specifiers

Not the fix

The public report with the same failure class was openai/codex#29639. Its workspace was stored under /home/... in the native WSL filesystem, while mine was exposed under /mnt/c/.... In both cases, a Windows .exe received a Linux-local sandboxCwd URI.

What I ruled out

I checked each boundary separately before changing the generated configuration.

The plugins were installed

  • ruled out

    Both Chrome and Computer Use were installed and enabled. Their tools appeared in the session.

    Shows This was not a missing-plugin failure.

The local MCP server received the request

  • ruled out

    Codex had generated a STDIO MCP configuration entry for the Windows executable. The file existed.

    Shows The structured MCP validation error showed that the process had started and received the request.

The workspace was reachable

  • ruled out

    WSL could read the workspace normally. Windows could address the same directory through a drive path or the WSL UNC namespace.

    Shows The directory existed; the receiver rejected the URI used to describe it.

Changing the MCP server's cwd did not change sandboxCwd

  • ruled out

    The rejected value came from MCP tool-call _meta, not from the process-level cwd in the generated MCP configuration.

    Shows Editing that generated block changed the wrong field, and Codex Desktop could overwrite it on restart.

Removing sandbox context was not the fix

  • ruled out

    Removing sandboxCwd would bypass this validation but omit the sandbox working directory from the request.

    Shows The fix needed to preserve that field and encode its value for Windows.

One directory, two local file URIs

One directory, two local file URIs: the Linux form and the form a Windows process needs
WorkspacePath in WSLURI local to LinuxURI for a Windows process
Mounted from a Windows driveFor a workspace mounted from a Windows drive, WSL sees:/mnt/c/work/projectThe Linux-local URI is:file:///mnt/c/work/projectrejected by WindowsA Windows process needs the drive form:file:///C:/work/projectWindows form
Stored inside the WSL filesystemA workspace stored inside the WSL filesystem needs a different translation. Linux may see:/home/user/projectnot given in this noteWindows reaches that directory through the WSL UNC namespace:file://wsl.localhost/<distro>/home/user/projectWindows form

Using /mnt/c was not itself the bug. The error came from sending a URI local to Linux to a process validating it under Windows rules.

Codex already knew the active WSL distribution and the MCP server's STDIO command. It did not combine those facts before attaching sandboxCwd.

The patch: map sandboxCwd only for Windows STDIO servers under WSL

I found a focused public fork commit that implemented the missing mapping: Nicolas0315/codex@1870471. I checked out the release used by Codex Desktop, applied the patch there, and kept the change scoped to this runtime boundary.

The patch:

  1. Stores each MCP server's STDIO command in its metadata.

  2. Detects whether Codex is running under WSL.

  3. Maps sandboxCwd only for a Windows .exe STDIO command launched through WSL interop.

  4. Maps /mnt/<drive>/... to a Windows drive file URI.

  5. Maps other WSL paths to a wsl.localhost UNC file URI.

  6. Leaves Linux commands, non-WSL hosts, Streamable HTTP MCP servers, and already compatible paths unchanged.

In reduced form:

Patch · reduced formrust
if is_wsl() && windows_exe_via_wsl_interop(stdio_command) {
    sandbox_cwd = map_wsl_uri_for_windows(sandbox_cwd, distro_name);
}

The server-type check prevents the mapping from changing requests sent to Linux MCP servers.

Version-pinned build and tests

I built from the release tag used by the desktop app with Cargo's lockfile unchanged. The adjacent Codex executables remained on that same release. Before the release build, I ran the test groups touched by the patch:

  • 6 passed

    WSL sandboxCwd mapping tests

  • 1 passed

    MCP server metadata regression test

  • passed

    Formatting and targeted fix checks

  • passed

    Locked release build

The mapping tests covered both workspace locations:

  • /mnt/c/... becomes file:///C:/....
  • /home/... becomes file://wsl.localhost/<distro>/home/....

The guardrail tests also kept the original URI for a Linux STDIO command and for a non-WSL host.

Fresh-process verification exposed two more boundaries

The running desktop process could reach Computer Use. That did not prove that a new CLI process could reconstruct the same chain from its saved configuration. Fresh-process testing separated four runtime roles that had looked like one configuration problem:

Four runtime roles that had looked like one configuration problem
Runtime roleUsesPlatform
WSL Codex callerpatched Linux Codex executableWSL
Windows MCP hostWindows node_repl.exeWindows
Windows sandbox helperWindows Codex executable via CODEX_CLI_PATHWindows
plugin module specifierWindows-valid file URLWindows

Plain codex still selected the stock launcher

The desktop app had an explicit path to the patched Linux executable. A plain codex command in WSL did not use that setting; shell resolution still selected the stock package launcher. I added a higher-priority CLI entry that points to the verified patched build and left the package-managed launchers installed as rollback paths.

This fixed launcher selection without replacing the package installation.

The Windows sandbox helper tried to execute a Linux binary

The Windows node_repl.exe creates its sandboxed Node child through a Codex helper selected by CODEX_CLI_PATH. Pointing that variable to the Linux executable produced Windows error 193: the file was a valid Linux ELF, but not a valid Win32 application.

The platform-specific executable roles therefore had to diverge:

The platform-specific executable roles
RoleExecutablePlatform
The WSL-hosted Codex calleruses the patched Linux executableWSL
The Windows MCP hostuses the bundled Windows node_repl.exeWindows
The helper selected by the Windows MCP environmentuses a Windows-native Codex executableWindows

I registered the command name codex-wsl-bridge on both platforms. WSL resolves it to the patched Linux build; Windows resolves it to the bundled PE. The generated configuration can therefore use one command name while each host runs the correct executable.

I kept the sandbox enabled. Disabling it also avoided the failing child process, but would have removed the isolation instead of fixing the executable boundary.

Dynamic imports needed Windows-valid module specifiers

The integration is installed as a plugin, but Windows Node loads the plugin's .mjs client with import(). Once Node started reliably, a module path discovered by the Linux process could still arrive as:

Path from the Linux processjs
await import("/mnt/c/.../computer-use-client.mjs")

Windows Node needs a Windows-valid file URL:

Windows-valid file URLjs
await import("file:///C:/.../computer-use-client.mjs")

The installed plugin instructions convert this path during normal use. I also added a narrowly matched, trusted PreToolUse path-rewrite hook for deterministic enforcement. The hook changes only /mnt/<drive>/... string literals inside dynamic imports sent to the Node REPL. It leaves shell paths, Linux-native tools, and unrelated JavaScript unchanged. Codex records trust against the exact hook definition, so a later edit disables it until the new definition is reviewed.

Checks after the build

The six WSL-to-Windows checks passed. Chrome required a separate recovery sequence and end-to-end browser check.

1. Direct MCP call

  • passed

    The original JavaScript probe ran and returned a Windows working directory.

    Shows The mapped sandboxCwd had passed request validation.

2. Plain CLI process from a Windows-mounted workspace

  • passed

    A new non-interactive codex process started from /mnt/c/..., reached the Windows Node runtime, and completed a Computer Use bootstrap without receiving a corrected path manually.

3. Plain CLI process from the native WSL filesystem

  • passed

    I repeated the fresh-process test from a native WSL directory. It reached the same sandboxed Windows runtime and returned the same Computer Use inventory.

    Shows This checked both workspace URI mappings rather than proving only the /mnt/c case.

4. Deliberately raw plugin import

  • passed

    I disabled the persistent mapping instruction for this probe, passed an exact raw /mnt/c/... dynamic import, and did not bypass hook trust. The trusted PreToolUse path-rewrite hook changed the call to file:///C:/... before Windows Node received it. The same import completed when the WSL-hosted CLI started from either a Windows-mounted workspace or the native WSL filesystem.

5. Computer Use application discovery

  • passed

    A read-only Computer Use discovery call returned 40 Windows applications. No app was launched or manipulated.

6. Post-restart acceptance check

  • passed

    The desktop app had been holding the previous Node transport in memory. After a full restart, the reopened app had a newer process start time and loaded the plugin client through file:///C:/.... A direct JavaScript probe returned a Windows working directory, and read-only Computer Use discovery again returned 40 applications. No app was launched or manipulated.

7. Chrome connection recovery and end-to-end check

  • not available

    Chrome as an available browser

    Chrome still did not appear as an available browser after the sandboxCwd fix.

These checks passed:

  • passed

    Chrome was installed and running.

  • passed

    The ChatGPT Chrome Extension was installed and enabled in the selected profile.

  • passed

    The native-messaging manifest was present and valid.

Recovery sequence, not a root-cause fix

  • availability returned

    Chrome availability returned after I reinstalled the plugin, reopened Codex, opened one blank Chrome window in the selected profile, and retried the connection.

    Does not show I did not identify which component had gone stale, so this remains a recovery sequence rather than a second root-cause fix.

  • passed

    End-to-end check

    A later end-to-end check used Codex to open a tab in the selected Chrome profile. A separate local Manifest V3 prototype then applied a reviewed native tab-group plan without closing tabs or reading page content. The prototype was an acceptance exercise, not part of the sandboxCwd fix or a second root-cause diagnosis.

These were separate failures. The URI mapping restored MCP request transport and Computer Use. The Chrome sequence restored the browser connection, and the tab-opening check verified it with a real browser action.

What I would check first next time

I would use this order of diagnosis again:

  1. Run the smallest MCP call. If request validation fails before user code, UI selectors, tabs, and clicks are downstream of the failure.

  2. Name both processes. “Codex on Windows” hides the useful detail: a Codex app server in WSL was calling a Windows .exe over STDIO.

  3. Inspect tool-call metadata and process configuration separately. A valid executable and process cwd do not make a foreign sandboxCwd URI valid.

  4. Preserve the sandbox context. Translate its working-directory URI for the receiver instead of removing it.

  5. Build the exact release. Desktop integrations can depend on matching executables and protocol versions.

  6. Check each layer. Test the URI mapping, platform-specific executable resolution, the Windows sandbox helper, a raw plugin import, Computer Use discovery, and the restarted desktop app.

  7. Keep separate failures separate. A fixed MCP request path can expose a later plugin-session problem. After recovery, verify one real browser action instead of stopping at availability.

Status and caveat

As of July 10, 2026, openai/codex#29639 remains open, has no linked pull request, and upstream main does not contain this mapping. The commit is a public fork patch rather than an official release.

I kept a rollback copy, pinned the build to the installed Codex version, and would replace it with an upstream release once the same mapping ships there.

For related Windows and WSL failures, see my Codex issue reproductions and debugging notes.

Credits

atlantho
published the anonymized reproduction in openai/codex#29639, including the native WSL /home/... case.
Nicolas0315
wrote the focused WSL-to-Windows sandboxCwd mapping patch used for the compatibility build.
OpenAI Codex
its open-source request path made it possible to inspect, test, and build the matching release.
Codex agent
helped trace the request metadata and repeat the Computer Use checks after the patched build was installed.