openai / openai/codex

[Windows]PerformanceCUA runtime materializer retries 4,680 doomed encrypted copy operations, causing ~8-minute first launch after updates

Open
#41,822 9 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

app bug computer-use performance windows-os
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

What version of the Codex App are you using (From “About Codex” dialog)?

Codex Desktop 26.825.6671.0 Microsoft Store / MSIX installation

What subscription do you have?

ChatGPT Plus

What platform is your computer?

Windows 11 x64 Microsoft Store / MSIX installation

What issue are you seeing?

After a Codex Desktop update, the first launch can remain headless for approximately 8 minutes before the Renderer appears.

I investigated the full startup timeline and then reproduced the CUA runtime materialization logic independently.

The delay is dominated by the Windows CUA Node runtime relocation step:

  • AppX Stage: ~5.9 s
  • AppX Register: ~5.1 s
  • CUA runtime materialization: 485 s
  • Runtime completion → first Renderer: ~2 s
  • Renderer → app-server: ~2 s

The runtime contains:

  • 4,680 files
  • 833 subdirectories
  • 334,374,010 bytes / 318.9 MiB

The important finding is that the delay is not caused by the amount of data being copied.

The Electron main-process materializer performs a synchronous, serial, depth-first copy of every file.

For each file it first calls:

copyFileSync(source, destination)

The MSIX source files carry the Windows Encrypted / Application Protected attribute.

On this machine, every one of the 4,680 copyFileSync calls fails with the Windows encrypted-copy failure path (errno=-4094, code=UNKNOWN; corresponding behavior is consistent with ERROR_ENCRYPTION_FAILED).

The code then falls back to:

writeFileSync(destination, readFileSync(source))

This fallback succeeds.

However, the materializer does not remember that the native copy path has already failed for this source tree. It repeats the same doomed copyFileSync attempt for every subsequent file.

So the effective algorithm is:

for each of 4,680 files:
    try copyFileSync()
        -> fails on Application Protected / encrypted MSIX source
    readFileSync()
    writeFileSync()

This happens synchronously on the Electron main process before the Renderer is created.

Reproduction / benchmark

I reproduced the same file tree outside the production runtime without modifying the installed Codex package or the real runtime.

All successful copies contained the same:

  • 4,680 files
  • 833 directories
  • 334,374,010 bytes

Results:

Method | Time -- | -- Real Codex first materialization | 485.0 s Exact reproduction of Codex materializer behavior | 429.685 s Node serial readFile + writeFile only | 19.195 s Node bounded parallel read/write, concurrency 32 | 9.075 s PowerShell/.NET serial read/write | 25.429 s

Native files account for about 72% of the bytes but less than 1% of the runtime.

This strongly indicates that the bottleneck is the number of failed copy operations, not disk throughput or total runtime size.

Materializer implementation details

The relevant materializer is in the Electron main-process bundle inside app.asar.

Observed behavior:

  1. Source: process.resourcesPath\cua_node
  2. Destination:
    %LOCALAPPDATA%\OpenAI\Codex\runtimes\cua_node\<16-hex-hash>
  3. A staging directory is created first.
  4. The directory tree is traversed synchronously and serially.
  5. For every file:
    • copyFileSync
    • on the encrypted/Application Protected copy failure:
      • readFileSync
      • writeFileSync
  6. After completion, staging is renamed to the final runtime directory.
  7. No full-tree SHA-256 verification is performed.
  8. Only three identity files are hashed; this takes about 0.13 seconds.
  9. No download or decompression occurs during this step.
  10. The synchronous materialization blocks creation of the Renderer.

The runtime hash calculated by this version is:

415ffebf3d576e9b

Steps to reproduce

  1. Install or update Codex Desktop through Microsoft Store/MSIX on Windows.
  2. Ensure the new version requires a new cua_node runtime hash.
  3. Launch Codex Desktop for the first time after the update.
  4. Observe that the main process starts but no Renderer appears.
  5. Monitor:
    %LOCALAPPDATA%\OpenAI\Codex\runtimes\cua_node
  6. Observe a staging runtime being populated for several minutes.
  7. After the runtime is complete, the Renderer appears almost immediately.
  8. A second launch is fast because the runtime is already present.

Expected behavior

Preparing a 318.9 MiB runtime should not block the Codex Desktop UI for several minutes.

Once Codex detects that copyFileSync cannot copy Application Protected / encrypted files from this MSIX source tree, it should avoid repeating the same failing operation for every remaining file.

Suggested minimal fix

The lowest-risk fix appears to be:

  1. Attempt the normal copy operation.
  2. On the first known encrypted/Application Protected copy failure (ERROR_ENCRYPTION_FAILED / equivalent Node error), switch the remainder of that runtime tree to the existing buffered read/write fallback.
  3. Do not retry the known-failing native copy path for another 4,679 files.

On this machine, simply skipping the repeated failing operations reduces the equivalent serial materialization benchmark from approximately:

430 seconds → 19 seconds

without changing the resulting file contents or runtime structure.

A later optimization could use bounded concurrent read/write operations. An equivalent 32-concurrency benchmark completed in approximately 9 seconds, although a lower concurrency may be preferable for production.

Another useful improvement would be a supported headless prepare-cua-runtime / bootstrap command so runtime preparation does not have to block the first interactive launch.

Additional information

No production Codex runtime, AppX package, app.asar, WindowsApps ACL, Defender configuration, registry entry, or encryption setting was modified during this investigation.

The production runtime remained intact.

Defender may still contribute some portion of the remaining difference between the 429.7-second reproduction and the 485-second real startup, but Defender is not required to explain the primary delay: reproducing Codex's own synchronous fail-then-fallback algorithm already reproduces 88.6% of the observed runtime.

This appears to be closely related to existing Windows MSIX / Application Protected runtime relocation reports, but this report adds a source-level performance root cause and quantitative reproduction showing that repeated failed copy attempts are responsible for most of the first-launch delay.

What steps can reproduce the bug?
  1. Install or update Codex Desktop through the Microsoft Store / MSIX package on Windows.

  2. Make sure the new Codex version requires a new CUA Node runtime hash under:

    %LOCALAPPDATA%\OpenAI\Codex\runtimes\cua_node

  3. Launch Codex Desktop for the first time after the update.

  4. Observe that ChatGPT.exe starts, but no Codex window appears for several minutes.

  5. Monitor:

    %LOCALAPPDATA%\OpenAI\Codex\runtimes\cua_node

    A new .staging--... directory is created and populated very slowly.

  6. On the affected system, the packaged files under:

    \app\resources\cua_node

    have the Windows Application Protected / Encrypted attribute.

  7. The Electron main-process materializer attempts copyFileSync() for every file.

    Each copy attempt fails with the encrypted-copy error path
    (Node errno -4094 / code UNKNOWN, consistent with Windows
    ERROR_ENCRYPTION_FAILED), then falls back to:

    readFileSync(source)
    writeFileSync(destination)

  8. This failed copy attempt is repeated independently for all 4,680 files.

  9. The runtime takes approximately 485 seconds to materialize.

  10. Once the runtime finishes, the Renderer appears about 2 seconds later
    and Codex starts normally.

  11. Close and reopen Codex without updating it.

    The second launch is fast because the valid CUA runtime already exists.

What is the expected behavior?

Codex Desktop should not block the first interactive launch for several minutes while preparing a ~319 MiB CUA runtime.

Once the application detects that the normal copy path cannot copy Application Protected / encrypted MSIX files, it should avoid retrying the same known-failing copy operation for every remaining file.

A minimal expected behavior would be:

  1. Attempt the normal copy operation.
  2. If the first file fails with the known encrypted/Application Protected copy error, switch the remaining runtime tree to the existing buffered read/write fallback.
  3. Continue preparing the runtime without repeating thousands of identical failed copy attempts.
  4. Show the Codex window promptly after runtime preparation completes.

On this machine:

  • Current behavior: ~485 seconds
  • Exact reproduction of current materializer logic: ~429.7 seconds
  • Serial read/write fallback only: ~19.2 seconds
  • Bounded parallel read/write: ~9.1 seconds

So the same runtime can be prepared in tens of seconds rather than several minutes without changing its contents or structure.

Ideally, runtime preparation should also either:

  • happen asynchronously after the main window is shown, or
  • have a supported headless/preparation path so first launch does not appear frozen.
Additional information

Additional diagnostics:

  • Codex Desktop version: 26.825.6671.0
  • Windows x64, Microsoft Store / MSIX installation
  • AppX Stage/Register complete successfully in ~11 seconds total
  • The long delay is isolated to CUA runtime materialization

Observed startup timeline:

  • Codex main process starts
  • New cua_node runtime staging begins ~10 seconds later
  • Runtime materialization takes ~485 seconds
  • Renderer appears ~2 seconds after materialization completes
  • app-server starts ~2 seconds after Renderer

The runtime contains:

  • 4,680 files
  • 833 subdirectories
  • 334,374,010 bytes (318.9 MiB)

Source and materialized runtime have matching file counts, directory counts and total bytes. The runtime identity hash is derived from manifest.json, bin/node.exe and bin/node_repl.exe.

The materializer is implemented in the Electron main-process bundle inside app.asar and performs synchronous, depth-first, serial file processing.

Exact reproduction of the same fail-then-fallback logic completed in ~429.7 seconds, reproducing 88.6% of the real startup delay.

Equivalent copy benchmarks:

  • Serial Node read/write: ~19.2 seconds
  • PowerShell/.NET serial read/write: ~25.4 seconds
  • Bounded parallel Node read/write (32 concurrency): ~9.1 seconds

The bottleneck scales with file count, not data size:
native binaries contain ~72% of the bytes but account for less than 1% of the reproduced materialization time.

No production AppX files, app.asar, WindowsApps ACLs, Defender settings, encryption attributes, or the existing CUA runtime were modified during testing.

Defender may contribute some additional latency, but it is not required to explain the primary issue: reproducing Codex's own synchronous copyFileSync-fail-then-read/write fallback path already reproduces most of the observed delay.

Possibly related issues:

  • #41170
  • #41540
    The most important observation is that the fallback itself is fast; the expensive part is retrying a copy operation that is already known to fail on this source tree.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

The relevant materializer is in the Electron main-process bundle inside app.asar; start by locating the synchronous traversal of process.resourcesPath\cua_node and its .staging destination. Reproduce the first-launch path on Windows MSIX, then verify that after the known encrypted-copy failure, remaining files use the existing readFileSync/writeFileSync fallback without repeated copyFileSync attempts.

Written by the indexing model from the issue text.

Assessment

Tech stack
electron, node.js
Domain
desktop, operating-systems, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.