~40s hang on startup (Windows)
What version of the Codex App are you using (From “About Codex” dialog)?
26.513.40821
What subscription do you have?
Enterprise
What platform is your computer?
Microsoft Windows NT 10.0.26100.0 x64
What issue are you seeing?
Codex not responding for about 40s any time I start it.
https://github.com/user-attachments/assets/6314a8f8-cb90-48d6-a976-090a176cf95f
What steps can reproduce the bug?
Start the app
What is the expected behavior?
It should let me use it in a reasonable amount of time after I open it.
Additional information
<details>
<summary>Agentic Analysis</summary>
Bug report: Codex Desktop locks up for ~42 seconds on cold launch (Windows)
Summary
On every cold launch of the Codex Desktop app on Windows, the UI is interactive but unresponsive — every renderer-to-backend RPC stalls for ~42 seconds before all completing simultaneously. Root cause is an unbounded Promise.all fan-out in the desktop's git-origins worker, which spawns ~3 × N git.exe processes (where N = recent workspaces). With Microsoft Defender real-time scan and Windows process-creation overhead, this dominates wall-clock time on launch and blocks the Electron main process's stdio reader, so unrelated backend RPCs queue behind it.
TL;DR
- Observed: ~30–40 s "locked" feel on app start.
- Measured: ~42 s RPC stall on every UI bootstrap call (
config/read,account/read,skills/list,model/list,hooks/list,windowsSandbox/readiness, …). - Cause:
worker.jsw6()doesPromise.all(dirs.map(...))over recent workspaces, each triggering 3git.exespawns. ~24 git invocations race against Defender on cold start. - Side cause: heavy worker activity blocks the Electron main process's stdio pipe reader, causing apparently-unrelated backend RPC responses to queue.
Environment
| Item | Value |
|---|---|
| Codex Desktop | 26.513.4821.0 (MSIX, installed at C:\Program Files\WindowsApps\OpenAI.Codex_26.513.4821.0_x64__<pkgid>\) |
| Bundled CLI (resources\codex.exe) | 0.131.0-alpha.9 (per [AppServerConnection] Current reported app-server version) |
| Recent workspaces | 8 (from globalState) |
| Repo dirs scanned | 6 .git repos under a single workspace root + 2 non-repo dirs (a user Downloads folder, a subfolder of a repo) |
| OS | Windows 11 Enterprise 10.0.26100 |
| Git | C:\Program Files\Git\cmd\git.exe (real .exe, not a .cmd shim) |
| Anti-malware | Microsoft Defender (enterprise managed) |
| Shell | PowerShell 7 (pwsh), profile on OneDrive |
| Remote SSH connections configured | 5 (4 × remote SSH hosts, 1 × WSL) |
Steps to reproduce
- Have a Codex Desktop install on Windows with several recent workspaces (≥ 4 git repos works).
- Close all Codex windows. Wait long enough that the
b6repo cache TTL (u6 = 24h) has expired — i.e. relaunching at least a day later, or after a reboot, or after killing allCodex.exeprocesses. - Launch Codex. Time the period from app window appearance to UI becoming responsive (e.g., clicking the model picker returns data).
Expected: a few seconds.
Actual: ~30–45 seconds of unresponsiveness.
Evidence from logs
Logs in %LOCALAPPDATA%\Packages\OpenAI.Codex_<id>\LocalCache\Local\Codex\Logs\…\codex-desktop-<sessionGuid>-<pid>-tN-iN-HHMMSS-0.log.
Timeline of one cold launch (process start at 15:59:25.440)
| t (s) | Event |
|---:|---|
| 0.0 | Launching app … platform=win32 packaged=true |
| 5.1 | warning Failed to load shell env caller=startup detail="Timed out after 5000ms." |
| 7.1 | [AppServerConnection] stdio_transport_spawned … codex.exe pid=30336 |
| 7.5 | initialize_handshake_result durationMs=373 outcome=success (backend healthy) |
| 8.6 | [startup][renderer] app routes mounted after 8634ms (UI mounted) |
| 13.5 | First batch of backend RPCs return in ~3–4 s — normal. |
| ~24 | git-origins worker-complete dirCount=2 elapsedMs=1119 hostId=local originCount=2 (first batch of 2 dirs — fast) |
| ~24 | git-origins worker-complete dirCount=1 elapsedMs=1065 hostId=local originCount=1 |
| 66.3 | git-origins worker-complete dirCount=8 elapsedMs=41981 hostId=local originCount=8 ← 42 s stall |
| ~66 | All pending RPCs return at once: config/read durationMs=45811, account/read durationMs=42737, skills/list durationMs=42745, model/list durationMs=42884, hooks/list durationMs=42736, windowsSandbox/readiness durationMs=42738, … |
Notable: every blocked RPC completes within the same 30 ms window once the git scan finishes. This is the giveaway that they were queued in the Electron main process's stdio reader, not actually slow on the backend.
Sample log lines
2026-05-17T16:00:31.718Z info [git] [git-origins] worker-complete dirCount=8 elapsedMs=41981 hostId=local originCount=8
2026-05-17T16:00:27.270Z info [AppServerConnection] response_routed … durationMs=45811 method=config/read
2026-05-17T16:00:27.271Z info [AppServerConnection] response_routed … durationMs=45789 method=experimentalFeature/enablement/set errorCode=-32600
2026-05-17T16:00:27.646Z info [AppServerConnection] response_routed … durationMs=42737 method=account/read
2026-05-17T16:00:27.647Z info [AppServerConnection] response_routed … durationMs=42745 method=skills/list
2026-05-17T16:00:27.648Z info [AppServerConnection] response_routed … durationMs=42737 method=config/read
2026-05-17T16:00:27.649Z info [AppServerConnection] response_routed … durationMs=42738 method=windowsSandbox/readiness
2026-05-17T16:00:27.649Z info [AppServerConnection] response_routed … durationMs=42736 method=hooks/list
2026-05-17T16:00:27.668Z info [AppServerConnection] response_routed … durationMs=42884 method=model/list
2026-05-17T15:59:44.902Z info [electron-message-handler] Skills/list request cwdsCount=8 forceReload=false
The 8 cwds (extracted from ~/.codex/.codex-global-state.json, paths anonymized):
<workspaces>\repo-a (git)
<workspaces>\parent-repo\subdir (no .git; subfolder of a repo)
<workspaces>\repo-b (git)
<workspaces>\repo-c (git)
<workspaces>\repo-d (git)
<workspaces>\repo-e (git)
<workspaces>\repo-f (git)
C:\Users\<user>\Downloads (no .git)
Root cause analysis
References below are to the formatted bundle from app.asar (resources\app.asar/.vite/build/worker.js and main-sqI8jfJr.js).
Call chain
- Renderer invalidates the
git-originsReact-Query key onactive-workspace-roots-updated/workspace-root-options-updatedevents fired during launch. - Main process IPC handler at
main-sqI8jfJr.js:21042—"git-origins": async ({ dirs, hostId }) => {…}. When the renderer passes no explicit dirs, it falls back toglobalStaterecent workspaces (all 8). Forwards to the per-host worker viathis.requestGitWorker({ method: 'git-origins', params: { dirs, … } }). - Worker dispatcher at
worker.js:59056—case 'git-origins':callsj6(dirs, gitManager, signal)then emits the[git-origins] worker-completelog line withelapsedMs. j6→M6→w6(worker.js:55787,55790,55756):
async function w6(e, t, n) {
return Promise.all(
e.map(async (e) => {
try { return await T6(nW(e), t, n); }
catch (t) { /* swallow, return null origin */ }
}),
);
}
This is the unbounded fan-out: no p-limit or semaphore. With 8 dirs that means 8 concurrent T6 chains.
T6(worker.js:55773) per-dir does:
let i = (await t.getWorktreeRepository(r, n))?.root ?? tW(e),
a = await t.getRepoRepository(r, n),
o = await a?.getOriginUrl(),
s = a?.getCommonDir() ?? null;
Underneath this requires three git.exe invocations per dir:
git rev-parse --show-toplevel(viaV2)git rev-parse --git-dir(viaH3)git config --get remote.origin.url(viad6atworker.js:55457, wrappinggetOriginUrlatworker.js:55646)
Plus one where.exe invocation per worker lifetime to locate git.exe (worker.js:53633 I2(), result cached in A2).
- Actual spawn in
$()atworker.js:53385viaS$({ args, cwd, env, … }).
Why this is so slow on Windows
| Cost | Approx |
|---|---:|
| CreateProcess + git.exe + DLL load | 80–150 ms |
| Defender real-time scan of git.exe | 200–400 ms |
| git config + credential helper init | 100–200 ms |
| .git/config + index read | 50–150 ms |
| Per git.exe invocation | ~400–800 ms |
8 dirs × 3 spawns = 24 invocations. Even with Promise.all, Defender serializes scans of the same image, and process-creation contention dominates. Empirically: 42 s wall-clock for the batch on this user's machine.
Secondary failure mode: main process backpressure
The Electron main process is single-threaded JavaScript. While the worker child process is producing output (spawning gits, parsing remotes, emitting log events back to main), the main process event loop services that traffic and cannot drain the stdio response pipe from codex.exe (the Rust app-server). Therefore all backend responses queue in the kernel pipe buffer until the worker work clears. This is visible as:
durationMs=45811 method=config/read
durationMs=42738 method=windowsSandbox/readiness
durationMs=42745 method=skills/list
durationMs=42884 method=model/list
— all completing within the same millisecond window even though the backend almost certainly answered them in milliseconds.
Other issues spotted in the same log (lower priority)
1. PowerShell shell-env probe times out hard at 5 s
warning Failed to load shell env caller=startup detail="Timed out after 5000ms." durationMs=5085
PowerShell 7 cold-start on Windows with a typical enterprise profile (oh-my-posh, mise, etc.) commonly exceeds 5 s. Measured on this machine: pwsh -NoProfile ≈ 520 ms, pwsh with profile ≈ 3.6 s — but on a fully cold launch with OneDrive-hosted profile and Defender scanning, it can blow past 5 s. The 5 s cap means Codex falls back to a stripped PATH and user-installed tooling won't be found. Suggest:
- Raise the cap to 10–15 s on Windows.
- Or load the env asynchronously and fix up tools later when it arrives, rather than blocking the startup path on it.
2. Protocol drift between desktop and bundled CLI
error Request failed … error={"code":-32600,
"message":"unsupported feature enablement `auth_elicitation`:
currently supported features are apps, memories, mentions_v2, plugins,
remote_control, tool_search, tool_suggest, tool_call_mcp_elicitation"}
method=experimentalFeature/enablement/set
The desktop (26.513.4821.0) is requesting an experimental feature (auth_elicitation) that the bundled CLI (0.131.0-alpha.9) doesn't know about. Five such calls fail with errorCode=-32600. They don't cause the lockup (the failure is fast — only their response routing is delayed by the same queue), but they're noise that suggests the bundled CLI may be older than the desktop expects.
Suggested fixes (in priority order)
Primary
- Bound concurrency in
w6's fan-out. Replace the barePromise.allwith a small concurrency limit (2–3). On Windows in particular, paralleling 24 process spawns gains very little because Defender scan-of-same-image serializes; meanwhile it starves the main process event loop. p-limit 2 should give nearly the same wall-clock with far less main-process pressure.
- Persist a (dir → originUrl, root, commonDir) cache to disk keyed by dir path and the mtime of
.git/config. On cold launch, render recents from the disk cache and refresh in the background. The in-process memoize (maxAge: 24hatworker.js:55648) is useless across process restarts.
- Defer the entire
git-originsscan until after first paint, or until the user opens the workspace picker. Recents can show their last known origin and refresh lazily.
- Filter the input list before scanning. Skip dirs that don't contain (or sit inside) a
.gitdirectory. The current input includesDownloadsand a non-repo subfolder. Doing a cheapfs.access(dir/.git)first would let the scan early-exit on those without spawning git.
- Move backend stdio reads off the same loop slot as worker IPC. Either give the AppServer transport a dedicated IO thread/worker, or
setImmediate-yield more aggressively while processing worker traffic, so other RPC traffic doesn't queue behind it. Even with (1)–(4), heavy workspaces will sometimes do real work; the desktop should not let that block all backend RPC traffic.
Secondary
- Increase the shell-env probe timeout on Windows to 10–15 s, or make it async and non-blocking on startup.
- Ship a matched desktop/CLI pair, or have the desktop gracefully no-op on features the CLI doesn't advertise (no
errorCode=-32600).
Workarounds (user-side, until fixed)
- Defender process exclusion for
git.exeandwhere.exe, and ideally the entire Codex install path. Single biggest knob on Windows. - Trim recent workspaces to the 1–2 you actively use.
- Make sure
gitin PATH is a real.exe, not a chocolatey/scoop.cmdshim (otherwiseworker.js:53627P2()wraps every git call incmd.exe /d /s /c). - Shrink your PowerShell
$PROFILE: lazy-loadmise activate, oh-my-posh init, etc. Aim for < 2 s. - Leave Codex running between sessions —
getOriginUrl's 24 h in-process memoize keeps the cache warm and avoids the cold-launch scan.
Verifying a fix
A simple regression test would replay the same git-origins cold-launch shape:
// pseudo-test
const dirs = recentWorkspaces(); // 8+ Windows paths
const t0 = Date.now();
await scanGitOrigins(dirs); // freshly seeded worker (no cache)
assert(Date.now() - t0 < 5000); // budget: 5 s on Windows w/ Defender
Acceptance: cold scan of 8 dirs completes in < 5 s on a Windows machine with Microsoft Defender real-time scanning active and git.exe not on the exclusion list.
Artifacts available
- Desktop logs:
%LOCALAPPDATA%\Packages\OpenAI.Codex_<id>\LocalCache\Local\Codex\Logs\… - Global state:
~/.codex/.codex-global-state.json(contains recent workspaces) - Extracted+formatted bundle for code references (from
app.asar): .vite/build/worker.js.vite/build/main-sqI8jfJr.js
</details>
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗