Windows App: WSL switch fails during provisioning, then hides history returned by thread/list
Environment
- Codex App: 26.707.62119
- MSIX package: OpenAI.Codex_26.707.8479.0_x64
- Codex CLI/app-server: 0.144.2
- Windows 11 Home x64, OS 10.0.26200 build 8655
- WSL 2.6.3.0
- Ubuntu 26.04 LTS
- WSL kernel 6.6.87.2-1
- /usr/bin/bash and bubblewrap 0.11.1 present
Summary
I enabled Run Codex in WSL because native Windows sandbox execution was slow. That native sandbox behavior is already covered by #32314, #31958, and #32168.
The WSL switch exposed a separate Codex App failure:
- On the first WSL launch, the App repeatedly terminated the WSL app-server after approximately 90 seconds while it was still performing healthy first-run provisioning.
- After provisioning completed, the same app-server initialized in under one second.
- Codex App then displayed no existing task history, even though a direct JSON-RPC thread/list request using the App's WSL configuration returned 953 existing threads.
- Reverting the App to native Windows immediately restored startup and the original history.
No task data was lost.
Why WSL was enabled
This is context rather than a second bug report:
- Native elevated mode currently takes approximately 3.5 seconds for a trivial command after workspace cleanup.
- Simple patch operations take approximately 10-16 seconds.
- Unelevated mode reduces a trivial command to approximately 0.2 seconds, but patching fails with:
~~~text
windows unelevated restricted-token sandbox cannot enforce split writable root sets directly; refusing to run unsandboxed
~~~
The detailed native sandbox reports are already in #32314, #31958, and #32168. WSL was selected as the practical workaround.
Observed WSL behavior
First launch fails during active provisioning
After enabling Run Codex in WSL and restarting the App, the WSL app-server was terminated after approximately 90 seconds before becoming reachable.
Diagnostics showed that it was not deadlocked or crashed. It was still performing one-time setup work, including:
- cold-starting WSL;
- loading the approximately 298 MB executable through /mnt/c;
- applying approximately 40 SQLite migrations; and
- backfilling approximately 950 existing threads.
Ubuntu had approximately 13 GB of available memory. There was no OOM, segmentation fault, kernel failure, or executable failure.
This is consistent with a fixed initialization deadline treating active first-run provisioning as a hung process.
Backend initializes, but the UI hides its history
After the one-time provisioning completed, a later launch initialized the exact WSL app-server in under one second.
Codex App opened, but its task history was empty.
A direct JSON-RPC thread/list request using the same WSL CODEX_HOME, app-server flags, and request parameters returned 953 existing thread records:
- 191 user threads
- 618 subagent threads
- 144 records with a null source classification
The databases and task files were present and readable. The backend returned the history, but the Desktop UI did not render it.
Persisted Windows cwd values use forms such as:
~~~text
C:\Users\<user>\...
~~~
while WSL represents the same locations as:
~~~text
/mnt/c/Users/<user>/...
~~~
This suggests a Desktop runtime/path/state classification problem rather than missing backend data.
Native rollback restores the App
Disabling Run Codex in WSL and restarting restored:
- normal App startup;
- the original task history; and
- access to the existing projects.
The WSL-side state was preserved for diagnostics.
Reproduction steps
- Start Codex App in native Windows mode with existing local task history.
- Configure Ubuntu as the default WSL2 distribution.
- Verify /usr/bin/bash and bubblewrap are available.
- Enable Run Codex in WSL.
- Restart Codex App.
- Observe the first launch fail after approximately 90 seconds while the WSL app-server is still migrating and backfilling its state.
- Restart after the provisioning work has completed.
- Observe that the WSL app-server initializes, but the App displays no existing history.
- Query the same app-server directly with thread/list and observe that it returns the existing threads.
- Disable Run Codex in WSL and restart.
- Observe that native startup and the original history return.
Expected behavior
- First-run WSL provisioning should report progress and should not be killed while migrations or backfill are advancing.
- The initialization timeout should reset on progress, allow sufficient first-run time, or retry safely.
- A failed WSL launch should offer an automatic rollback to native Windows.
- Threads returned by thread/list should appear in the Desktop UI.
- Windows and WSL cwd forms should be normalized consistently when grouping tasks under projects.
- Switching environments must not make preserved task history appear lost.
Relationship to suggested duplicates
- #32314 and #31958 cover the native elevated sandbox slowdown that motivated the WSL switch.
- #32168 covers the unelevated split writable-root failure and is already closed as a duplicate.
- #32182 reports one missing task after an App update. It does not involve switching to WSL, a first-run provisioning timeout, or a backend thread/list response containing the hidden history.
- #30288 is related, but describes a short WSL eligibility timeout followed by fallback to the Windows app-server. In this reproduction, the WSL app-server launched, performed provisioning, and was terminated around the App's later initialization boundary.
- #24364 is also related to Windows/WSL cwd migration. In this reproduction, an unfiltered thread/list request returned the complete WSL history while the Desktop UI remained empty.
Diagnostic material
Sanitized App and sandbox logs can be supplied privately through /feedback. Raw SQLite databases, task JSONL files, user paths, and conversation content are intentionally not attached publicly.
2 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Adding data on this from a newer build — App
26.715.4045.0, app-server0.145.0-alpha.18, Windows 11 Pro 26200, WSL 2.7.10.0, OracleLinux 9.5 (kernel 6.18.33.2), bubblewrap 0.6.3, roughly 393 rollout files / 547 MB of session history, 413 threads. Both failure modes @Cameloo1 reported on 26.707 / 0.144.2 are still present several builds later.There's an exact mechanism behind the first one, and a harder variant of the second.
1. The provisioning kill has a permanent form: the backfill claim goes stale
The report describes the process being killed while it was actively working. The mechanism is visible in the app-server log:
The claim lives in
backfill_stateinstate_5.sqlite. When a run is interrupted, that row is left atstatus='running'withlast_success_atstill NULL, and nothing expires it — there's no heartbeat or TTL. Every subsequent launch reads the row, concludes a backfill is already running elsewhere, waits its 30 seconds, and performs no work itself. The owner it waits for is long dead.In the original report provisioning eventually completed. Here it could not, and the app became permanently unlaunchable: main, GPU, network and storage processes all start, crashpad runs, everything reports
Responding=True, but no--type=rendererprocess is ever spawned, so no window appears. No crash dumps, nothing in the Windows event log. From a user's perspective, clicking the icon does nothing at all, indefinitely — which makes this very hard to self-diagnose.Relaunching does not help and cannot: four consecutive launches moved
last_watermarkby zero bytes, because no launch does any work while the stale claim is held. Clearing the claim while preserving the watermark resolves it:The backfill then resumed from its watermark and completed in under 40 seconds —
status='complete', 413 threads, matching the Windows-side DB exactly. The next launch producedinitialize_handshake_result outcome=successin 949 ms andwindow ready-to-showafter 161 ms. Deleting the row instead also clears the lock, but discardslast_watermarkand forces a full rescan over/mnt/c.For what it's worth, the 30s value appears to be hardcoded — no
CODEX_*env var, config key or CLI flag in the binary touches it. #23787 mentions a 900-second backend lease against the 30-second GUI wait, which is consistent with the behaviour here.2. Path normalization: UNC workspace roots break
turn/startentirelyThe path-normalization theory in the report holds, and there's a sharper case than
C:\...vs/mnt/c/.... With a workspace root stored as a UNC path (\\wsl.localhost\<distro>\home\<user>\src\repos\<repo>), every message submit failed:A UNC path isn't absolute to the Linux app-server — absolute means a leading
/— so it's treated as relative and joined onto the server's own working directory. That's what "without a base path" means here: not a notation mismatch, but a relative path with nothing to resolve it against. The consequence goes beyond hidden history: with a UNC root the app is unusable, since every message fails atturn/start.Rewriting the stored roots into Linux notation resolved it —
active-workspace-roots,electron-saved-workspace-roots,thread-workspace-root-hintsandlocal-projectsin.codex-global-state.json, plus matching[projects.'/linux/path']trust entries inconfig.toml. (The app rewritesconfig.tomlfrom its own state on exit, so it has to be closed first.) Afterwards:turn/startwitherrorCode=nulland noAbsolutePathBuferrors at all.Two observations that may help scope the fix:
method=fs/watchearlier the same day in native Windows mode with that UNC root open. UNC roots trip path handling in more than one code path, not only under WSL.thread not foundis returned for a number of older conversation IDs while those threads are present in the DB — likely the same underlying issue as the hidden-history symptom in the report, surfacing through a different call.Findings that look relevant but aren't
The sandbox log fills with
GetSecurityInfo failed: 1/GetNamedSecurityInfoW failed: 1on the WSL UNC path. These are noise — 9p doesn't implement Windows ACLs, and they appear daily including on days when everything works (305 on one entirely healthy day). Likewisebundled_executable_relocation_failedforbwrap: the bundled copy really is missing from the MSIX, but the distro's/usr/bin/bwrapis present and this is not what terminates the server. A 519 MBlogs_2.sqliteis a genuine concern (#27741) but was not causal here either.Suggested fixes
runningrow whose owner no longer exists should expire rather than deadlock every future launch — this is what turns a slow first run into a permanently broken install.\\wsl.localhost\root onto the server's cwd is the worst of the available outcomes — rejecting it with a clear message would already be a large improvement.AbsolutePathBuf deserialized without a base pathin a toast is unactionable; the log contained the answer immediately.