fix(ext/napi): clear error for Windows addons that link against node.exe (#34696)
## Problem
Loading uWebSockets.js (and similar prebuilt native addons) on
**Windows** fails with a cryptic error:
```
error: Uncaught (in promise) Error: This version of uWS.js supports only Node.js LTS versions 16, 18 and 20 ...
TypeError: LoadLibraryExW failed
```
The "supports only Node.js LTS" text is uWS's own fallback message; the
real error is `LoadLibraryExW failed`, which tells the user nothing
actionable.
## Root cause
uWS's prebuilt Windows `.node` (`uws_win32_x64_{108,115,127}.node`) is
**not a Node-API addon**. Inspecting its PE import table (`objdump -p`)
shows a **regular, non delay-load import of `node.exe`** that pulls in:
- raw **V8 C++ ABI** symbols (`v8::Object::New`, `HandleScope`,
`FunctionTemplate`, …)
- **Node.js internals** (`node::MakeCallback`,
`node::AddEnvironmentCleanupHook`, `node::GetCurrentEventLoop`)
- **libuv** (`uv_run`, `uv_async_init`, …) and **zlib**
The Windows loader resolves an import of `node.exe` by module name at
`LoadLibraryExW` time, so it only succeeds when the host process is
literally named `node.exe`. Loaded into `deno.exe` (or any other host —
this is also why it breaks in Node SEA, Bun and Electron), the import
can't be satisfied and the load fails.
Deno cannot make such an addon work: it doesn't (and can't) export V8's
C++ ABI, Node's internal C++ API, or a real libuv event loop. The only
correct outcome is a clear diagnostic.
## Fix
Mirrors the legacy V8/nan addon diagnostic added in #34683.
- New dependency-free, bounds-checked PE import-table reader in
`ext/napi/pe.rs`. `imports_node_executable()` parses the **regular**
import directory and checks for a `node.exe` import (case-insensitive).
It is unit-tested with a synthetic PE builder and was validated against
the real uWS binaries (output matches `objdump` exactly).
- New `NApiError::UnsupportedNodeBinaryAddon`. On Windows, when
`Library::load_with_flags` fails, the addon is inspected; if it imports
`node.exe` directly, Deno now reports:
> Cannot load native addon at <path>: it links directly against
the Node.js binary (node.exe) and relies on the V8 C++ ABI, Node.js
internals and/or libuv exported by that executable, none of which Deno
provides. Only Node-API (N-API) addons are supported. ...
**Delay-loaded** `node.exe` imports (the node-gyp default, redirected to
the host at runtime via `win_delay_load_hook`) are intentionally ignored
— those resolve against the host's exported N-API symbols and work fine
in Deno.
## Testing
- `cargo test -p deno_napi --lib pe::` — 4 unit tests
(real-binary-shaped fixtures + malformed input).
- `cargo clippy -p deno_napi --all-targets` and `cargo clippy -p
deno_napi --target x86_64-pc-windows-msvc` both clean (the new
load-failure arm is Windows-only).
Refs #25956
Closes denoland/divybot#409
---------
Co-authored-by: divybot <divybot@users.noreply.github.com>
Co-authored-by: Divy Srivastava <me@littledivy.com> E
em committed
9d13e9d37f76d53389199c313f383597f4faecba
Parent: ccfe78a
Committed by GitHub <noreply@github.com>
on 6/2/2026, 7:04:04 AM