Codex on WSL and Windows, part 2: fixing a missing browser service
The ChatGPT extension reported that Chrome was connected, but a Codex browser call failed before reaching it. The Windows runtime tried to import a browser service from a plugin-cache directory that did not exist.
The current service was present in the installed Codex app. It was also present in the directory prepared for bundled-plugin installation. The installation step between that directory and the active cache had failed.
This is part two of Fixing Codex Computer Use Across WSL and Windows. The first investigation dealt with Linux file URIs reaching Windows processes. This one followed a shared marketplace registration through the desktop app, the plugin installer, and the browser runtime. The repair required no executable patch and no change to the import hook from part one.
The failure happened before Chrome
The browser call returned this error. User-specific directories are replaced with placeholders throughout this article:
Cannot find module
'<CODEX_HOME>\plugins\cache\openai-bundled\browser\26.901.41123\scripts\browser-service.mjs'
imported from <TEMP>\<worker-directory>\trusted-worker.js
The desktop app was version 26.901.5003.0. Its bundled Browser component identified itself as 26.901.41123. Those are different version identifiers for the app package and its component, not evidence of a mismatch by themselves.
The mismatch was between the component selected by the runtime and the installed plugin cache. The cache contained only Browser version 26.727.40816.
A host-side file check confirmed that the requested cache entry was absent. This mattered because a sandbox access failure alone would not establish that a Windows host file was missing.
At this point, the extension's connection indicator could not answer the relevant question. Chrome might be connected to its native host while the Codex task still fails to load the service needed to use that connection.
Why was the task loading code from a cache?
The word “cache” initially made the path look accidental. Inspection of the installed desktop code showed that it was deliberate. The app constructs a service path with this shape:
<CODEX_HOME>/plugins/cache/<marketplace>/<plugin>/<version>/scripts/browser-service.mjs
There were three copies or destinations to distinguish:
- The application bundle contained the shipped Browser plugin and its service file.
- The prepared marketplace directory, under
.tmp/bundled-marketplaces/openai-bundled, contained the current files that the plugin installer could consume. - The installed plugin cache, under
plugins/cache/openai-bundled/browser, contained the older installed version.
The current files had reached the first two locations. They had not reached the third.
The task's Windows JavaScript runtime received a NODE_REPL_TRUSTED_SERVICES environment setting. Its browser entry pointed at the versioned cache path. The trusted worker reads this mapping and imports the configured service when the browser client calls it.
That also explains why importing a client from the Chrome plugin did not avoid the failure. The client and the service are separate modules. Choosing the client import does not replace the service mapping supplied by the desktop app.
The same mapping appeared in the shared .codex/config.toml. This was an application configuration problem encountered by one task. There was no evidence of a special redirect created for that conversation. Other tasks could still differ because their processes might already have loaded a service.
Was the import hook changing the path?
Part one introduced a PreToolUse hook to normalize dynamic imports passed from WSL to Windows Node. That made it a reasonable suspect.
The complete hook source was short enough to inspect directly. It reads JSON from standard input, looks at tool_input.code, and rewrites literal dynamic imports beginning with /mnt/<drive>/.
For example:
await import("/mnt/c/work/plugin/browser-client.mjs")
becomes:
await import("file:///C:/work/plugin/browser-client.mjs")
The hook does not choose a plugin version, write configuration, change environment variables, or delete files. The failing browser call already used a Windows drive path, which its rewrite rule would leave unchanged.
The missing service was imported later, inside the trusted worker, using NODE_REPL_TRUSTED_SERVICES. That was outside the hook's input-rewrite operation.
This source inspection did not establish whether the Linux-shaped hook command executed successfully in this Windows task. It did establish that its rewrite logic did not construct the failing service path. Disabling it would leave that path and the missing cache entry in place.
The startup log exposed the installation failure
The shared configuration registered the bundled marketplace using its WSL path:
[marketplaces.openai-bundled]
source_type = "local"
source = "/mnt/c/Users/<user>/.codex/.tmp/bundled-marketplaces/openai-bundled"
The Windows desktop tried to register the corresponding Windows directory:
C:\Users\<user>\.codex\.tmp\bundled-marketplaces\openai-bundled
The log recorded the rejection:
marketplace 'openai-bundled' is already added from a different source;
remove it before adding this source
These paths referred to the same underlying directory through different operating-system conventions. The marketplace registration did not accept them as the same source.
On September 5, the startup sequence was:
05:10:42.619 UTC Current bundled marketplace files written
05:10:42.725 UTC Marketplace registration rejected
05:10:43.391 UTC Browser runtime paths selected
The installed desktop code supported the ordering seen in the log. Its marketplace setup first tries to register the prepared source. If registration fails, it returns a failure result or throws before reaching plugin installation and reconciliation.
The browser configuration code still constructs the service path from the app's current component version. In this run, that left the runtime expecting 26.901.41123 while installation was blocked and the cache remained older.
One startup message said bundled_plugins_reconcile_completed after the earlier registration error. Later retries explicitly logged bundled_plugins_reconcile_failed. A completion message on its own was therefore insufficient evidence that the plugins had installed.
The first path change was incomplete
I first changed the registered source from the WSL form to a Windows path with forward slashes:
C:/Users/<user>/.codex/.tmp/bundled-marketplaces/openai-bundled
That was enough for explicit Browser and Chrome installation to succeed. It did not fully repair marketplace reconciliation.
A final check attempted to register the exact Windows source used by the desktop. Codex still returned the “different source” error. Browser access could work after installation while a later automatic plugin update remained blocked by the registration mismatch.
The supported marketplace commands resolved this second detail. Removing and re-adding only the openai-bundled source let Codex persist its own canonical Windows representation:
source = '\\?\C:\Users\<user>\.codex\.tmp\bundled-marketplaces\openai-bundled'
The \\?\ prefix is the Windows extended-length path form. Repeating the same add command then returned alreadyAdded: true. The desktop's next reconciliation accepted the source as well.
This was why a valid filesystem path was not enough: registration also depended on the stored representation used to identify the source.
The repair commands
These commands describe the Windows-native repair on this installation. They are not a general instruction to rewrite a Linux-native Codex setup.
Before changing anything, I backed up the shared configuration and confirmed the intended CODEX_HOME, the bundled marketplace directory, and the Browser and Chrome manifests. The local bundle already contained the matching official versions; no package was requested as latest.
In PowerShell, with the intended Windows Codex executable selected:
codex plugin marketplace list
# Use the actual Windows CODEX_HOME if it differs from this default.
$codexConfigRoot = Join-Path $env:USERPROFILE '.codex'
$bundledSource = Join-Path $codexConfigRoot '.tmp\bundled-marketplaces\openai-bundled'
codex plugin marketplace remove openai-bundled --json
codex plugin marketplace add $bundledSource --json
codex plugin add browser@openai-bundled --json
codex plugin add chrome@openai-bundled --json
codex plugin marketplace add $bundledSource --json
codex plugin list --marketplace openai-bundled --json
The sequence above puts the complete source repair before installation. During the investigation, the explicit installations happened after the incomplete forward-slash edit; the remove/add correction followed when the final registration check caught the remaining error.
marketplace remove removed the source registration. This repair did not use plugin remove, delete broad cache directories, replace the whole configuration, or disable the hook.
Both plugin installations reported 26.901.41123, and the service file existed at the exact path requested by the runtime. Comparing the final configuration with the backup showed the source replacement and removal of the old marketplace update metadata. The hook and unrelated settings were preserved.
The runtime needed one more refresh
The first browser retry still returned the earlier missing-module error even though the file now existed. The running JavaScript kernel retained the failed service load.
Resetting that task's kernel and loading the newly installed client cleared the failure. This reset affected the task's JavaScript bindings; it did not require restarting Codex Desktop or Chrome.
The supported browser runtime then selected Chrome through the extension connection. Verification went beyond the connection indicator:
- List the existing Chrome tabs.
- Open a temporary tab at this site's CV page.
- Read its title and rendered content through the accessibility API.
- Close the temporary tab.
- Confirm that the existing Chrome session remained accessible after the final source registration.
The desktop logs now showed successful registration followed by reconciliation:
06:21:20.983 UTC bundled_plugins_marketplace_added
06:21:21.157 UTC bundled_plugin_install_skipped_current pluginName=chrome
06:21:22.623 UTC bundled_plugins_reconcile_completed
This time the completion event followed an accepted source registration, and a real browser read had succeeded.
What this repair establishes
The observed failure crossed several boundaries: shared Windows/WSL configuration, marketplace source identity, plugin installation, the service import, and the Chrome connection. The logs and code inspection connected the first four. Opening and reading a page verified the last one.
The repair restored this Windows desktop task's Chrome access. It did not test native Linux plugin discovery with the same shared configuration, prove that every other task had identical live settings, or identify who originally wrote the WSL marketplace source. Future tooling that writes the WSL source back into the shared registration could recreate the conflict.
For this failure, the useful checks were the exact configured service path, the files present in each installation location, the marketplace registration result, and one real browser action. Each answered a different question that the extension's “Connected” label could not answer alone.
Sources and scope
This note is based on local configuration, the installed Codex application's code, bundled plugin manifests, September 5 startup logs, CLI installation receipts, and the browser verification described above. Paths and log excerpts are sanitized. The implementation findings describe the inspected build, not a guarantee about later releases.
- Part one: Fixing Codex Computer Use Across WSL and Windows covers the earlier URI mapping and import hook.
- OpenAI's Codex MCP documentation describes the local MCP configuration used by integrations.
- The Codex agent helped inspect the hook and runtime, trace the registration failure, apply the reviewed repair, and verify Chrome access.