ARM64 Windows: desktop app crashes ~16s after every launch - Codex Micro serialport addon delay-load failure (0xC06D007F)

Resolved 💬 4 comments Opened Jul 15, 2026 by TerraSYNCBIIZ Closed Jul 15, 2026
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

ARM64 Windows: ChatGPT desktop crashes ~16s after every launch — Codex Micro serialport addon delay-load failure (0xC06D007F)

Summary

On ARM64 Windows, the ChatGPT desktop app's main (browser) process dies ~16 seconds after every launch. The window appears, then vanishes. It is 100% reproducible.

The cause is the bundled Work Louder / Codex Micro serialport native addon. It delay-imports napi_create_function from node.exe, which node-gyp's delay-load hook resolves to the host executable (ChatGPT.exe). ChatGPT.exe exports no N-API symbols — they live in chrome.dll. The lookup fails, the Visual C++ delay-loader raises 0xC06D007F, and because it's an SEH exception at dlopen time, the app's JS try/catch cannot catch it. The process dies.

Impact

Any ARM64 Windows user with Codex Micro enabled cannot open the app at all. First crash on this machine: 2026-07-15 ~11:52 ET, with no local change — consistent with a server-side rollout of the Codex Micro feature. The app was working normally before that.

Environment

| | |
|---|---|
| App | ChatGPT desktop (OpenAI.Codex) 26.707.9981.0, arm64, MSIX |
| ChatGPT.exe | 150.0.7871.115, built 2026-07-07 |
| Client version (logs) | 26.707.72221 |
| OS | Windows 11 build 26220, ARM64 (Surface Pro X / Snapdragon, 8 cores) |
| Serial devices | 6 COM ports present (4x Surface UART, 2x Bluetooth SPP) |

Root cause

Decoded DelayLoadInfo from the Crashpad minidump:

exception code : 0xC06D007F     = VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND)
szDll          : "node.exe"
szProcName     : "napi_create_function"
fImportByName  : 1
hmodCur        : 0x7FF658A80000  -> ...\OpenAI.Codex_26.707.9981.0_arm64__2p2nqsd0c76g0\app\ChatGPT.exe
pfnCur         : 0x0
dwLastError    : 127             (ERROR_PROC_NOT_FOUND)
ptype          : browser         (main process -> whole app dies)

The szDll / szProcName strings live in the .rdata of:

app\resources\app.asar.unpacked\node_modules\@worklouder\device-kit-oai\
  node_modules\@worklouder\wl-device-kit\node_modules\serialport\
  node_modules\@serialport\bindings-cpp\prebuilds\win32-arm64\node.napi.node

Export tables confirm the mismatch:

| Binary | Total exports | N-API exports | napi_create_function? |
|---|---|---|---|
| ChatGPT.exe | 2 | 0 | NO |
| chrome.dll | 3174 | 158 | YES |
| chrome_elf.dll | 27 | 0 | NO |

node-gyp's stock win_delay_load_hook.cc intercepts the node.exe load and returns GetModuleHandle(NULL) — the host exe. In standard Electron that exe exports the N-API symbols. In this Chromium-shell architecture they're in chrome.dll, so the hook resolves to a module that cannot satisfy the import.

The first-party addons are built correctly and load fine — better_sqlite3.node, windows-account.node, windows-updater.node are all loaded in the same process at crash time. Only the vendored third-party prebuild is wrong. win32-arm64 is the only prebuild shipped, so there is no fallback.

Trigger path

renderer -> IPC -> CodexMicroHostService.getState()
  -> eS.getService()
     -> require('./codex-micro-service-CR6sUcZG.js')
        -> top-level: createRequire(__filename)('@worklouder/device-kit-oai')
           -> @worklouder/wl-device-kit -> serialport -> @serialport/bindings-cpp
              -> LoadLibrary(prebuilds/win32-arm64/node.napi.node)
                 -> delay-load napi_create_function from ChatGPT.exe -> NULL -> 0xC06D007F -> fatal

The try/catch around discovery.findWLDevices([DeviceType.Project2077]) cannot help — the crash happens earlier, during require, and is a native SEH exception.

Reproduction

  1. ARM64 Windows 11, ChatGPT desktop 26.707.9981.0, Codex Micro enabled.
  2. Launch the app.
  3. ~16s later the main process dies; a Crashpad dump lands in

%LOCALAPPDATA%\Packages\OpenAI.Codex_2p2nqsd0c76g0\LocalCache\Roaming\Codex\web\Codex\Crashpad\reports.

Observed 3/3 launches, identical signature each time (dumps at 15:21:44, 15:36:45, 16:00:05).

Suggested fix

  1. Rebuild/relink the @serialport/bindings-cpp win32-arm64 prebuild against the host that actually exports the N-API symbols (chrome.dll), matching how the first-party addons are built — e.g. a custom delay-load hook returning GetModuleHandle("chrome.dll") instead of GetModuleHandle(NULL).
  2. node-hid's HID.node sits in the same @worklouder tree and is very likely to hit the identical failure once serialport is fixed — please check it too.
  3. Defensive: wrap the codex-micro-service require so a failure degrades the feature instead of killing the main process. (Note: an SEH delay-load failure is not catchable from JS — the real fix must be #1.)
  4. Short term: consider gating the Codex Micro rollout off for ARM64 Windows until the addon is rebuilt. This unblocks affected users immediately.

Note

There is no user-side workaround. The broken file is inside the signed MSIX package, and Windows blocks modification even for an elevated Administrator that has taken ownership.

View original on GitHub ↗

4 Comments

github-actions[bot] contributor · 1 month ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #33381
  • #33384
  • #33385
  • #33392
  • #33409

Powered by Codex Action

TerraSYNCBIIZ · 1 month ago

Correction from the reporter: this is not ARM64-specific, and the root cause is the link method — please don't triage it as an arch/packaging fault

I filed this as an ARM64 problem. Further investigation shows that framing is wrong, and acting on it would produce a fix that doesn't fix anything. Corrections below, with the evidence.

1. x64 has the identical defect — binary-verified

I pulled the real x64 package from Microsoft's FE3/DeliveryOptimization CDN (OpenAI.Codex_26.707.9981.0_x64__2p2nqsd0c76g0, 728,668,849 bytes, AppxManifest ProcessorArchitecture="x64", same build number) and parsed it:

| Binary (x64 package) | Exports | N-API exports | napi_create_function? |
|---|---|---|---|
| ChatGPT.exe | 2 (GetHandleVerifier, IsSandboxedProcess) | 0 | NO |
| chrome.dll | 3174 | 145 | YES |

And the x64 serialport binary delay-imports 53 napi symbols from node.exe, including napi_create_function — the exact fatal linkage. The trigger JS is byte-identical (same Vite content hash codex-micro-service-CR6sUcZG.js).

The 2-export stub launcher is a property of the Chromium exe/chrome.dll split. It is not arch-conditional. Any x64 user in the gate's rollout crashes identically.

2. The real discriminator: normal-import vs delay-import

Parsing the import tables of every native addon in app.asar.unpacked (note: the serialport path is 286 chars — os.walk and PowerShell -Recurse silently miss it; use \\?\):

| Addon | Linkage | Result |
|---|---|---|
| better_sqlite3.node | NORMAL import from chrome.dll (74 fns) | works |
| pty.node | NORMAL import from chrome.dll (38 napi incl. napi_create_function) | works |
| @worklouder/.../@serialport/bindings-cpp/prebuilds/win32-arm64/node.napi.node | DELAY import from node.exe (52 napi) | fatal |
| @worklouder/.../node-hid/build/Release/HID.node | DELAY import from node.exe | fatal |

Your first-party addons were electron-rebuilt and link chrome.dll directly. The vendored @worklouder/device-kit-oai subtree was never electron-rebuilt — it ships stock npm artifacts that use node-gyp's win_delay_load_hook, which resolves node.exe → GetModuleHandle(NULL) → ChatGPT.exe → 0 napi exports → ERROR_PROC_NOT_FOUND (127) → 0xC06D007F.

node-hid's HID.node carries the same defect and will fail the same way once serialport is fixed. Both need rebuilding.

3. Not a "9981.0 crashes on launch" bug — it's gate-triggered

The renderer gates the whole path on a Statsig gate. In app-main-DkkOUoXp.js:

let c = ba(`3207467860`)          // ba <- statsig-C0dzr8Jc.js
d = c ? jsxs(Fragment, { children: [ jsx(dJ /*CodexMicroBridge*/), jsx(pJ /*CodexMicroOnboardingHost*/) ] }) : null

CodexMicroBridge is the only boot-path caller of codexMicro.getState(), which reaches:

// codex-micro-service-CR6sUcZG.js, module scope -- not lazy, not guarded
var { ..., WLDeviceDiscovery: l } = (0, n.createRequire)(__filename)(`@worklouder/device-kit-oai`)

Gate false → chunk never imported → no crash. Gate true → deterministic death ~16s after launch. Statsig fails closed, and the gate value is not persisted anywhere on disk, which is exactly why this is reboot-immune, reinstall-immune, and unfixable by the user.

Note the try/catch around discovery.findWLDevices([DeviceType.Project2077]) cannot help — the fault is an SEH raised during dlopen, before any JS frame exists to catch it.

4. Suggested fix, in order

  1. Immediate mitigation: flip gate 3207467860 off for the affected cohort. That unblocks every affected user right now, on both architectures, with no client change.
  2. Real fix: electron-rebuild the @worklouder/device-kit-oai subtree (both @serialport/bindings-cpp and node-hid) so they normal-import chrome.dll like better_sqlite3/pty already do — or give them a delay-load hook returning GetModuleHandle("chrome.dll") instead of GetModuleHandle(NULL).
  3. Defense in depth: the arm64 package ships only prebuilds/win32-arm64 with no fallback, so there is nothing for node-gyp-build to fall back to. Consider failing the require gracefully rather than letting an unrebuilt addon take down the browser process.

5. Environment note

There is no user-side workaround. The addon lives inside the signed MSIX; Windows refuses modification even for an elevated Administrator who has taken ownership and granted themselves FullControl (no DENY ACEs, Controlled Folder Access off). Uninstall/reinstall returns the identical build. The Store serves no other version — FE3 exposes exactly one revision, and the separate "ChatGPT (Beta)" listing (OpenAI.CodexBeta 26.707.3351.0) ships the identical defect.

Affected users are simply stuck until the gate flips or a rebuilt package ships.

TerraSYNCBIIZ · 1 month ago

Closing as a duplicate of #33381 (ARM64 crash-loop) — the consolidated thread has the full analysis and @aonbrowser's working preload mitigation, which I've confirmed on my hardware.

Leaving one finding here that neither thread has, since it changes the scope of the fix.

#33375 and #33381 are the same defect on different architectures — and they ship different broken artifacts

#33375 (x64, "severe UI lag" + recurring Application Error 1000) and #33381 (ARM64, hard crash-loop) are the same bug. The differing symptom is just when the fatal delay-load lands: at boot on ARM64, on a retry path on x64.

I pulled the real x64 package from Microsoft's FE3/DeliveryOptimization CDN (OpenAI.Codex_26.707.9981.0_x64__2p2nqsd0c76g0, 728,668,849 bytes, AppxManifest ProcessorArchitecture="x64") and parsed it alongside the installed arm64 build:

| Binary | arm64 | x64 |
|---|---|---|
| ChatGPT.exe exports | 2 (GetHandleVerifier, IsSandboxedProcess), 0 napi | 2 (identical), 0 napi |
| chrome.dll napi exports | 158 | 145 — napi_create_function present |
| serialport addon | delay-imports node.exe, 52 napi | delay-imports node.exe, 53 napi |

The 2-export stub launcher is a property of the Chromium exe/chrome.dll split and is not arch-conditional. So win_delay_load_hook → GetModuleHandle(NULL) → ChatGPT.exe → ERROR_PROC_NOT_FOUND fails identically on both.

The scope-changing part — the two arches ship different artifacts:

arm64:  @serialport/bindings-cpp/prebuilds/win32-arm64/node.napi.node   (npm prebuild)
x64:    @serialport/bindings-cpp/build/Release/serialport.node          (locally built)

Different files, same defect. Rebuilding only the arm64 prebuild will leave x64 broken, and vice versa. node-hid's HID.node in the same @worklouder subtree carries the identical delay-import and needs the same treatment.

The discriminator is link method, not architecture — verified across every native addon in the package:

| Addon | Linkage | Result |
|---|---|---|
| better_sqlite3.node | NORMAL import ← chrome.dll | works |
| pty.node | NORMAL import ← chrome.dll | works |
| @worklouder/…/serialport | DELAY import ← node.exe | fatal |
| @worklouder/…/node-hid HID.node | DELAY import ← node.exe | fatal |

First-party addons were electron-rebuilt. The vendored @worklouder/device-kit-oai subtree was not.

Also ruled out, with evidence

  • Downgrade is impossible. Raw FE3 SOAP exposes exactly one revision (26.707.9981.0, rev 1, x64 + arm64). There is no archive. The separate "ChatGPT (Beta)" listing (OpenAI.CodexBeta 26.707.3351.0, PE timestamp 19h before the stable build) ships the identical @worklouder defect — not a downgrade.
  • Editing the package is impossible. Windows refuses to rename the addon even for an elevated Administrator who has taken ownership and holds FullControl, with no DENY ACEs and Controlled Folder Access off.
  • Reinstall is a no-op. The Store serves the identical build.

Gate 3207467860 rollback remains the correct immediate vendor mitigation — it fixes both architectures at once with no client change.

TerraSYNCBIIZ · 1 month ago

Duplicate of #33381.