-
-
Notifications
You must be signed in to change notification settings - Fork 31
win-arm64 Node 20 binary built without ICU/snapshots, breaks pkg at runtime #152
Description
Problem
The pre-built Node.js v20.11.1 binary for win-arm64 (in the v3.5 release) is built with --without-intl, which disables ICU, V8 snapshots, and code cache. This makes the binary unusable with @yao-pkg/pkg — any packaged executable silently exits with code 4 (InternalJSEvaluationFailure) on Windows ARM64.
The win-x64 binary is built with --with-intl=small-icu and works correctly.
Build config comparison
| Config key | win-x64 | win-arm64 |
|---|---|---|
node_use_node_snapshot |
true |
false |
node_use_node_code_cache |
true |
false |
icu_small |
true |
false |
v8_enable_i18n_support |
1 |
0 |
Root cause
In lib/build.ts, compileOnWindows():
if (
major < 24 &&
hostArch !== targetArch &&
!config_flags.includes('--with-intl=full-icu')
) {
config_flags.push('--without-intl');
}When cross-compiling from x64 → arm64 for Node < 24, ICU is completely disabled. This was added in commit 748f67a as a workaround because small-icu cross-compilation failed.
Without ICU, V8 snapshots can't be generated. Without snapshots, the pkg bootstrap module (internal/bootstrap/pkg.js) must be evaluated fresh from source at runtime. This evaluation triggers prepareMainThreadExecution() which loads internal modules that depend on ICU/Intl — causing InternalJSEvaluationFailure (exit code 4) with no output.
Why x64 works
The x64 binary has node_use_node_snapshot: true. The V8 snapshot was created at build time with full ICU support, so all bootstrap evaluation is pre-baked. The pkg VFS mechanism works through the snapshot path.
Impact
This affects all consumers of pkg that ship win-arm64 executables, including pnpm (pnpm/pnpm#9207). The arm64 exe files are published in releases but are completely non-functional.
Suggested fix
Since pkg-fetch now has access to native windows-11-arm GitHub Actions runners (per recent workflow changes), the Node 20 arm64 binary could be rebuilt natively with --with-intl=small-icu instead of cross-compiling with --without-intl.
Alternatively, using --with-intl=full-icu for cross-compilation would work (larger binary but functional).
Reproduction
# Download the v3.5 release binaries
gh release download v3.5 --repo yao-pkg/pkg-fetch -p "node-v20.11.1-win-arm64" -p "node-v20.11.1-win-x64"
# Check configs (on any machine with python3)
python3 -c "
data = open('node-v20.11.1-win-arm64', 'rb').read()
print('arm64:', 'node_use_node_snapshot' in str(data))
idx = data.find(b'node_use_node_snapshot')
print(data[idx-5:idx+50])
"
# Shows: "node_use_node_snapshot": false
# Then package any trivial app and run the arm64 exe on a Windows ARM64 machine
# → exits with code 4, no output