Repository navigation
[Windows][26.924.2738.0] Startup spinner forever in most launches: renderer root never mounts; restarting child codex.exe fixes it (analysis + workaround) #48896
Description
What version of the Codex App are you using (From “About Codex” dialog)?
26.924.22138 (Microsoft Store package OpenAI.Codex_26.924.2738.0_x64), app-server 0.158.0-alpha.2.1
What subscription do you have?
ChatGPT Plus
What platform is your computer?
Microsoft Windows NT 10.0.26200.0 x64 (Windows 11 Pro 25H2, build 26200.9550)
What issue are you seeing?
After signing in, the app stays on the loading spinner forever in most launches: 8 of 10 signed-in launches in my logs hung, 2 loaded normally. Same symptom as #48558, #48333, #48615, #48364, #48522. I'm filing separately because I have a root-cause analysis from the logs and the bundled code, plus a reliable automated workaround.
The backend is healthy in hung launches:
[startup] Host startup critical-path phases completed[AppServerConnection] Codex CLI initialized- renderer requests are answered in under 20 ms (
config/read,account/read,getAuthStatus,windowsSandbox/readiness,experimentalFeature/list) [statsig-refresh-diagnostics] React root render requestedandchatgpt_pubsub_topic_subscribedare logged
But [startup][renderer] app routes mounted never appears, and the renderer goes quiet after that.
What I found (desktop logs plus reading app.asar of 26.924.2738.0):
- The spinner is the Suspense fallback
debugName: "Startup"that wraps the renderer root (Dioinwebview/assets/app-initial-ff48311587c5.js). On desktopstartupReadyis undefined (electronBridgehas nostartup), so the boundary is held by a child that suspends and never resolves. - The
codex-app-server-connection-changedlistener (PRa, which logsremote_connections.connection_state_changed) is inside that boundary. In every hung launch the renderer never logs aconnection_state_changedforhostId=local, not even a snapshot. In every successful launch it logsstate=connected1–3 s before routes mount. - The launches that succeed are the slower ones (ms from process start):
| launch | Codex runtime config selected |
pubsub_topic_subscribed |
app routes mounted |
|---|---|---|---|
| ok | 7910 | 12776 | 12776 |
| ok | 6465 | 12670 | 12672 |
| hung | 6035 | 10657 | never |
| hung | 5666 | 10013 | never |
| hung | 5502 | 9523 | never |
| hung | 5259 | 9096 | never |
| hung | 5227 | 9089 | never |
| hung | 5035 | 8926 | never |
| hung | 4931 | 8856 | never |
| hung | 4711 | 8402 | never |
This looks like a startup race: something the root waits for fires before the renderer subscribes to it. Restarting the app-server re-emits the connection events, and the root mounts right away (see the workaround below).
Ruled out (each one tested, the app still hangs): reinstall via winget (msstore source); resetting .codex-global-state.json; moving LocalCache\Roaming\Codex\web aside; clearing ~/.codex/plugins/cache/openai-primary-runtime (3 hung launches right after a fresh plugin install); reboot; network (no proxy, chatgpt.com and persistent.oaistatic.com reachable). Sandboxing is not the cause either. The app hung the same way with the default elevated Windows sandbox, and I switched it off only to rule it out (the logs above are with sandbox_mode = "danger-full-access" and [windows] sandbox = "unelevated"). windowsSandbox/readiness answers in about 10 ms.
What steps can reproduce the bug?
- Install 26.924.2738.0 from the Microsoft Store on Windows 11 and sign in.
- Quit the app completely (no
ChatGPT.exeprocesses left). - Launch it again. The spinner stays forever in about 80% of launches here, so repeating steps 2–3 reproduces it within a few tries.
What is the expected behavior?
The main window finishes loading within a few seconds on every launch.
Additional information
Reliable workaround: while the spinner is shown, terminate the child codex.exe (the app-server spawned by the main ChatGPT.exe). The app respawns it (app_server_restart_recovery_done in the log) and the UI mounts within about 3 s.
Get-CimInstance Win32_Process -Filter "Name='codex.exe'" | ? { (Get-Process -Id $_.ParentProcessId -EA 0).Name -eq 'ChatGPT' } | % { Stop-Process -Id $_.ProcessId -Force }I automated it with a launcher that starts or focuses the app, waits up to 15 s for app routes mounted … rendererWindowAppearance=primary in the log of the current main PID, and otherwise restarts the app-server (up to 3 times). It runs from a shortcut: conhost.exe --headless powershell.exe -NoProfile -ExecutionPolicy Bypass -File launch-chatgpt.ps1. Tested on a cold start (UI up in about 20–30 s) and on an already-running app (no-op).
launch-chatgpt.ps1
$app = 'shell:AppsFolder\OpenAI.Codex_2p2nqsd0c76g0!App'
$logRoot = Join-Path $env:LOCALAPPDATA 'Packages\OpenAI.Codex_2p2nqsd0c76g0\LocalCache\Local\Codex\Logs'
$mountTimeoutSec = 15
$maxRestarts = 3
function Get-AppMain {
Get-CimInstance Win32_Process -Filter "Name='ChatGPT.exe'" |
Where-Object { $_.ExecutablePath -match '\\OpenAI\.Codex_' -and $_.CommandLine -notmatch '--type=' } |
Select-Object -First 1
}
function Test-Mounted($mainPid) {
$log = Get-ChildItem -LiteralPath $logRoot -Recurse -Filter "*-$mainPid-t0-*.log" -ErrorAction SilentlyContinue |
Sort-Object CreationTime -Descending | Select-Object -First 1
$log -and (Select-String -LiteralPath $log.FullName -Pattern 'app routes mounted.*rendererWindowAppearance=primary' -Quiet)
}
Start-Process $app # launches the app, or focuses it if it is already running
$main = $null
for ($i = 0; $i -lt 30 -and -not $main; $i++) { Start-Sleep -Milliseconds 500; $main = Get-AppMain }
if (-not $main) { return }
for ($restart = 0; $restart -le $maxRestarts; $restart++) {
$deadline = (Get-Date).AddSeconds($mountTimeoutSec)
while ((Get-Date) -lt $deadline) {
if (Test-Mounted $main.ProcessId) { return }
if (-not (Get-Process -Id $main.ProcessId -ErrorAction SilentlyContinue)) { return } # app was closed
Start-Sleep -Seconds 1
}
if ($restart -eq $maxRestarts) { return }
# Still on the spinner: restart the app-server; the app respawns it and the UI proceeds.
Get-CimInstance Win32_Process -Filter "Name='codex.exe'" |
Where-Object { $_.ParentProcessId -eq $main.ProcessId } |
ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }
}Possibly a separate bug, hit on the first sign-in: right after login the primary runtime switched from latest 26.923.10815 to alpha 26.915.20218 and re-downloaded it (about 5 min, while the UI showed the spinner). The post-install plugin sync then failed with failed to back up plugin cache entry: Access is denied. (os error 5) (errorCategory=plugin_cache_windows_file_lock, failureStage=sync_plugins), because the running app holds the plugin files.
Activity
github-actions commentedon Sep 28, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
mumuhylsf-prog commentedon Sep 29, 2026
Additional observations from Windows 11:
The startup behavior described here matches what I experienced. When the desktop app was stuck loading, terminating only the child
codex.exeprocess allowed the UI to open. The problem returned after closing and reopening the app.I can also reproduce a related freeze after the app has opened. While a local task was running, I selected text and clicked “More details” to open a companion conversation. The panel remained on its loading indicator, and the entire desktop UI stopped responding. Even the task timer stopped updating, and Remote became unavailable. The app remained frozen for at least 3–4 minutes; only the Windows minimize, maximize, and close buttons responded.
At the time, the frozen UI made it impossible to tell whether the local task was still running. I later observed new files being generated, suggesting that the task continued despite the UI freeze. I did not terminate
codex.exeduring that active task because doing so might interrupt it.The startup hang and this in-session freeze may be related, but I cannot confirm that they have the same root cause.
mumuhylsf-prog commentedon Sep 29, 2026
This also appears to reproduce on Windows, as reported in #48711. In my case the UI freezes immediately after clicking More Details, before sending anything.
top7845-source commentedon Sep 29, 2026
I can now reproduce this consistently.
Environment:
On a clean local profile with no restored old sessions:
codex.exeprocess spawned byChatGPT.execauses the app-server to respawn.codex.exeagain restores the UI.codex doctorreports 0 failures, successful WebSocket connectivity (HTTP 101 Switching Protocols), healthy local databases, working authentication, and reachable provider endpoints.The issue also reproduces after a fresh Windows installation, clean app install, and without restoring any previous
.codexchats/session data.This appears to match the startup race described in this issue.
hjxccc commentedon Sep 29, 2026
Same issue here — adding data points that narrow it down.
Env: Windows 11 Home 10.0.26100,
OpenAI.Codex 26.924.2738.0, app-server0.158.0-alpha.2.1, ChatGPT sign-in, behind a local HTTP proxy (Clash).Frequency: 8 of 15 launches on 2026-09-29 hung. Same signature as this issue:
codex-home request hostId=undefined, then noremote_connections.manager_state_set hostId=local, noapp routes mounted. Every OK launch logsconnection_state_changed ... hostId=local state=connectedshortly before mounting.Backend is fine in hung launches (
~/.codex/logs_2.sqlite):/codex/models200, remote-plugin sync completes, remote-control websocket reachesConnected. Renderer net breadcrumbs (Sentry scope) are all 200.Ruled out (each still hung afterwards):
Connectedafter ~4 s, no request errors).selected-remote-host-idin.codex-global-state.json(pointed at a remote-control env no longer on the account) — removed, still hung.remote_control_enabled=0instate_5.sqliteso the websocket doesn't auto-connect at startup — still hung (desktop re-enables it viaremoteControl/enable).Code reading agrees with the race described here:
AppServerConnection.setConnectionStateonly broadcastscodex-app-server-connection-changedon an actual state change, andsendConnectionStateSnapshotis sent fromregisterWebviewWindowat window creation — both can happen before the renderer's listener (inside theStartupSuspense boundary) subscribes. A late subscriber has no way to pull the current state, so it waits forever unless something else changes the state (e.g. restarting the childcodex.exe).tonyfenwick8814121 commentedon Sep 30, 2026
Additional evidence from a newer Windows build, investigated on 2026-10-01 (UTC+8). This looks related to the startup state-delivery problem, but I cannot confirm an identical root cause.
Environment
OpenAI.Codex 26.928.2636.0.26.928.21956; Electron42.3.0.0.159.2.Observed failure and recovery
vscode/codex-homestartup reads werepending/fetchingwith no data. Reissuing the exact read-only requests returned real data. Six other stalled local startup reads were also reissued successfully, but the logo remained.gatewayOAuthReadiness: the renderer's localappServerVersionwas stillnull, so its gateway capability/readiness check could not proceed. This was not evidence of a failed gateway sign-in.codex-app-server-restart,hostId: "local",intent: "restart",killCodexProcess: false) caused real initialization state to arrive. The renderer then reported0.159.2, the logo disappeared, and the sidebar/composer rendered normally. Composer loading and resume-pending flags were false.There was no cache wipe, authentication reset, conversation deletion, reinstall, or manual fabrication of readiness data. No draft was submitted as a test.
Potentially less disruptive recovery mechanism
The native handler in this build already accepts:
{"type":"ready","initializationOnly":true}Code inspection shows that it replays connection and initialization snapshots to the requesting renderer without restarting the service.
Update: fresh-launch recurrence on 2026-10-01. The compatibility launcher successfully recovered seven startup reads, but issued zero initialization snapshot requests and expired after three minutes with the logo still present. Inspection again showed the renderer waiting on a null local app-server version. Manually sending the message above to that hung renderer delivered the real version
0.159.2and restored the sidebar/composer without a service restart. This verifies snapshot replay against an actual recurrence after startup-read recovery; it does not establish that replay alone can repair every cold-start hang.The launcher's snapshot trigger depended on locating an internal React scope and observing exactly a null version. I removed that dependency: a persistent logo with missing or temporarily unobservable version state can request the real snapshot; a known valid version suppresses it. Focused regression checks cover unavailable/delayed scope discovery, normal-window no-op behavior, exact read-only query matching, retry caps, and the three-minute lifetime. The exact timing that prevented scope/version detection in the failed run has not been proven.
I also found a separate launcher bug: after failure, a modal notice retained the recovery mutex indefinitely. The failed helper was still alive roughly twenty minutes later, so subsequent invocations exited as duplicates. The helper now releases the mutex before displaying a notice, and notices close after eight seconds. After stopping only the verified stale helper, the revised launcher successfully connected to the running application, recovered one remaining startup read, and exited with
ready, zero errors, no logo and a rendered composer in ten seconds. This running-window check did not interrupt the model service and is not a cold-launch test. These internal message contracts are version-specific and this is a compatibility workaround, not a proven permanent fix.A local compatibility launcher limits startup-only retries to three minutes, with at most three retries per exact read-only query/snapshot request. Its already-running/healthy path was verified to perform no repairs. End-to-end cold-launch verification of the revised automatic trigger is still pending; another user task is active, so I am not presenting regression tests or manual recovery as that verification.
The model service was usable while the renderer was hung. Separate real request telemetry after restart shows
transport="responses_websocket"for the previously HTTP-fallback task as well as the diagnostic task. Renderer startup state delivery and model transport fallback therefore need to be diagnosed separately in this occurrence.Data-corruption checks
Read-only SQLite
quick_checkreturnedokfor 12 core local databases; the TOML configuration and persisted global-state JSON parsed successfully. Recent inspected logs did not show SQLite corruption, invalid TOML, or disk-full signatures. These checks do not prove that all historical records are semantically valid, but UI recovery without removing those records argues against bulk history deletion as the necessary fix for this occurrence.Earlier occurrences on September's 26.908/26.917 builds blocked existing-conversation submission at
loading-local-configuntil the local directory reads were reissued. The newer logo stall additionally required initialization-state recovery.Could the desktop subscribe before requesting the initial connection/version snapshot, provide a replay when that state remains missing, and turn stalled startup reads into a bounded, recoverable error instead of an indefinite logo?