Repository navigation
[Windows] Cold launch hangs forever on loading indicator; renderer ignores its own reload but recovers instantly on external CDP Page.reload #48878
Description
What version of the Codex App are you using?
26.924.2738.0 (app build 11645, Codex Desktop/26.924.22138)
What subscription do you have?
ChatGPT Plus
What platform is your computer?
Microsoft Windows NT 10.0.26200.0 x64 (Windows 11 25H2)
What issue are you seeing?
On a cold launch the app frequently hangs permanently on the loading spinner. The
window is responsive at OS level (Responding = True) but the renderer never recovers
by itself.
Measured state while hung (CDP attached to the main window, app://-/index.html):
document.getElementById('root').innerHTML.length= 1236document.querySelectorAll('*').length= 55- exactly 1 element with an
animate-spinclass (the spinner);#rootinnerText is empty
The renderer is completely idle while hung:
- 0 network requests over 90 seconds (
Network.enableactive) - 0 messages on the preload worker channel over 5 seconds
Debugger.pauseyields only empty/minified frames — no app code executing, no pending timers- DOM byte-identical across 9 samples 10 s apart (90 s total)
The main process is healthy at the same time; these preload bridge calls all return in
0–7 ms with real data:
electronBridge.getInitialSidebarBootstrap()→ real catalog entries and projectselectronBridge.getAppSessionId()→ valid UUIDelectronBridge.getDesktopUserAgent()→Codex Desktop/26.924.22138 (Windows NT 10.0; x64)
Statsig is also fine: window.__STATSIG__.firstInstance.loadingStatus === "Ready".
The key finding: the app's own reload is dead, an external reload works
While hung:
| Action | Result |
|---|---|
F5 sent to the focused window (keybd_event after SetForegroundWindow) |
no effect, DOM unchanged after 25 s |
Ctrl+R sent the same way |
no effect, DOM unchanged after 25 s |
External CDP Page.reload({ignoreCache:true}) |
works — full UI renders, #root grows to ~129 000 chars / ~1100 nodes |
The UI only appears 10–25 seconds after the external reload is issued (measured
progression: 4 264 → 1 236 → 99 979 → 126 098 chars), so any check that polls for less
than ~25 s will wrongly conclude the reload failed.
What steps can reproduce the bug?
- Fully exit the app (kill all
ChatGPT.exeandcodex.exeprocesses). - Wait 10–15 seconds for the processes to fully exit.
- Launch it from the Start menu / taskbar.
- Observe that the loading spinner never resolves.
It is intermittent — some cold launches succeed. In my testing roughly 3 of 4 cold
launches hang.
What is the expected behavior?
The app should finish initializing on cold launch, and pressing F5 / Ctrl+R
(or View → Reload) should recover the window if it does get stuck.
Anything else?
Environment was ruled out with measurements:
- The app works immediately after a reload, so the package and its files are intact.
Reinstalling the MSIX package does not help. - Network is fine:
chatgpt.comreturns HTTP 200 with the full page over the same
proxy path the app uses; TCP/TLS tochatgpt.com,ab.chatgpt.com,api.openai.com,
auth.openai.com,persistent.oaistatic.comall succeed. Switching VPN providers and
rolling back the proxy client made no difference. CBS.logreportsRepairNeeded: no— system image healthy.- The account is authenticated (
account/readsucceeds; tokens refresh normally).
Possibly related entries from the app's own logs
(%LOCALAPPDATA%\Packages\OpenAI.Codex_*\LocalCache\Local\Codex\Logs), may or may not
be relevant:
[electron-message-handler] Request failed method=account/read
error={"code":-32603,"message":"workspace routing discovery timed out"} durationMs=15019
[electron-message-handler] sa_server_request_failed
errorMessage="Workspace routing is unavailable" status=432 url=/wham/usage
[electron-message-handler] Statsig: error while bootstrapping post-login client
errorMessage="Timed out while fetching post-login Statsig bootstrap"
This React Router warning fires on every launch, including successful ones, so it is
probably a red herring — included for completeness:
Matched leaf route at location "/" does not have an element or Component.
This means it will render an <Outlet /> with a null value by default resulting in an "empty" page.
Workaround
Killing all ChatGPT.exe / codex.exe processes, relaunching with
--remote-debugging-port=9222, and issuing a CDP Page.reload recovers the app in
about 4 seconds, every time.
Note that a plain taskkill /F /IM "ChatGPT.exe" /T often does not terminate all
processes; a Stop-Process -Force round is sometimes required before the app will
accept the --remote-debugging-port argument, because the single-instance mechanism
otherwise swallows it.
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
kyleisbased commentedon Sep 29, 2026
Independent confirmation of this failure signature, plus a launcher-integrated workaround that has been reliable across cold starts on a Windows 11 Store/MSIX installation.
Observed failure signature
On a hung cold launch, attached over CDP to the main window (
app://-/index.html):That matches the measurements in the report above exactly, including
innerHTML.length = 1236. The window is OS-responsive, the app-server completes its handshake and answersconfig/read,account/read,experimentalFeature/list,plugin/installedandskills/listin well under 50 ms each, and auth is valid — but the renderer never sends the follow-up work (nothread/list) and the UI stays on the spinner.Consistent with the analysis already posted in this thread, the renderer appears to lose the initialization snapshot because it arrives before the renderer's message listeners are registered, and is not replayed or buffered.
Two things I can confirm as not causal on my machine:
Matched leaf route at location "/" does not have an element or Component) fires on working and hung launches alike. It is a red herring, as noted above.config.tomlcontent does not control it. I A/B tested a comment-only config, a regenerated config, a deleted config, and an unchanged frozen config across repeated cold launches. A rendered run and a hung run ended up with byte-identical config files (same SHA-256). Removingconfig.tomloccasionally yields one working launch because it forces a fresh handshake — the same reason an external reload works — not because of its contents.A practical detail that cost me several failed attempts
The UI can take a long time to actually paint after the reload is issued. My first attempts polled for ~20 s and concluded failure, then gave up. Any verification loop needs to allow at least 25–30 s after
Page.reloadbefore deciding it did not work. Intermittency is also real: I measured roughly 3 of 4 cold launches hanging, consistent with the report above.Workaround
The approach is the one already described (launch with
--remote-debugging-port, then drive CDP). What I added, which is what made it dependable as a daily launcher rather than a manual recovery step:#rootchild count, spinner count, error-page detection) rather than a loose "is there any text" check. I had an early version that reported success against the browser's own error page because the error text exceeded the threshold, and a separate version that hung indefinitely because the CDP calls had no timeouts.PowerShell launcher. It reuses an existing session if one is already running, otherwise starts the app with the debug port and repairs it:
repair.mjs— targets the main window explicitly, re-requests the initialization snapshot first (works for some reporters, not on my build), then falls back to a reload, and allows 40 s for the UI to appear:probe.mjs— the pass/fail check. Only the main window counts; the app always has background windows (detached-window.html,avatar-overlayviainitialRoute) that legitimately never paint, and judging those makes the result meaningless:Measured results
With the version that fired the repair immediately after the debug port opened: 0 of 4 cold starts rendered. With the wait-then-retry version: 7 of 7 cold starts rendered, and after consolidating the scripts, a further 3 of 3.
Caveats
127.0.0.1while the app runs, which is not an acceptable permanent state.Thanks to everyone who documented the mechanism above — the listener-timing measurements in this thread are what made this reproducible rather than guesswork.