ReviewOS

craft-native/craft

fix(windows): WebView2 environment creation fails with CO_E_NOTINITIALIZED — the runtime never initialises COM

#335
Closed glennmichael123 opened this 8 days ago ·
8 days ago

Summary

craft.exe cannot open a window on Windows. CreateCoreWebView2Environment fails with 0x800401f0 (CO_E_NOTINITIALIZED) because the runtime never initialises COM on the thread that calls it.

The import table shows it: craft.exe links ole32.dll for exactly one function, CoTaskMemFree, and imports no CoInitializeEx, CoInitialize or OleInitialize. That is true of v0.0.100 and of v0.0.140, so it is not a regression — I could not find a Windows release that does initialise COM.

What happens

⚡ Loading URL in native window: http://127.0.0.1:63854/app
   Title: SystemCleaner
   Size: 1400x900

[WebView2] Environment creation failed: 0x800401f0
[WebView2] Initialization failed
[2026-10-12T13:04:02Z] ERROR WebView2InitFailed

The process then exits. No window is ever created.

Reproduction

craft.exe http://127.0.0.1:8080/ --title Example --persistent-storage --width 1400 --height 900 --no-devtools
  • Windows Server 2025 Datacenter, build 26100 (the windows-latest GitHub Actions runner)
  • WebView2 Runtime 153.0.4234.48 installed — read from HKLM:\SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}, so this is not a missing-runtime problem
  • craft.exe and WebView2Loader.dll taken straight from craft-windows-x64.zip, unmodified

Evidence

Parsing the PE import directory of the shipped binaries:

=== v0.0.100
  ole32.dll: CoTaskMemFree
  KERNEL32.dll: 74 imports
  USER32.dll: 22 imports
  ntdll.dll: 59 imports
  ...

=== v0.0.140
  ole32.dll: CoTaskMemFree
  KERNEL32.dll: 78 imports
  USER32.dll: 41 imports
  ntdll.dll: 59 imports
  ...

CreateCoreWebView2Environment requires an initialised COM apartment on the calling thread, and 0x800401f0 is precisely the error for calling it without one.

(WebView2Loader.dll is absent from the import table because it is loaded with LoadLibrary/GetProcAddress, which is the normal loader pattern and not related to this.)

Suggested fix

Call CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED) on the UI thread before creating the WebView2 environment, and CoUninitialize on the way out. WebView2 wants an STA. OleInitialize would also do it and additionally sets up OLE drag-and-drop, which a browser host usually wants.

Also worth a look: the v0.0.90 Windows archive

Unrelated to the above, found while pinning a version. craft-windows-x64.zip for v0.0.90 contains only craft.exe — no WebView2Loader.dll, which craft.exe loads by name at runtime. Every release I checked from v0.0.100 on ships both. A build pinned to v0.0.90 therefore installs cleanly and then cannot create a webview at all.

Context

Found while building a Windows desktop bundle for stacksjs/system-cleaner (tracking issue stacksjs/system-cleaner#23). Everything on our side reaches the runtime correctly — the loopback agent binds, its health endpoint answers, and craft.exe receives the URL, title and size shown above — so the failure is isolated to WebView2 environment creation.

Happy to test a patched build against the same CI job if that is useful.

8 days ago

Resolved on main by e2fc920 (test(windows): guard WebView2 COM initialization). The Windows host initializes COM before creating the WebView2 environment, with a regression guard in the Windows test path.

Sign in to comment on this issue.