fix(core): warn loudly when falling back to the WebAssembly runtime - #36596
fix(core): warn loudly when falling back to the WebAssembly runtime#36596llwt wants to merge 3 commits into
Conversation
When the platform native binary is missing from node_modules, the generated native-bindings.js silently falls back to the bundled WASM runtime. That fallback always succeeds, so nx keeps running but the workspace context and hasher can spin at 100% CPU for hours with no output, which reads as a hang rather than a broken install. The one existing WASM warning lives behind the daemon-enabled check, and in CI the isCI/isDocker branch above it wins first, so it never fires in the environment where this actually bites. Warn on stderr as soon as IS_WASM is observed, naming the missing platform package and the lockfile cause behind it. The warning uses a synchronous fd write because a queued async write cannot drain once the process blocks in synchronous WASM work, and dedupes through NX_WASM_FALLBACK_WARNED so the processes nx spawns do not each repeat it. NX_ALLOW_WASM_FALLBACK=true silences it, and genuine WASM-only platforms stay quiet. Also surface the runtime in nx report so the fallback is visible when diagnosing after the fact.
❌ Deploy Preview for nx-docs failed. Why did it fail? →
|
✅ Deploy Preview for nx-dev ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
View your CI Pipeline Execution ↗ for commit 94ac920
☁️ Nx Cloud last updated this comment at |
Co-authored-by: llwt <llwt@users.noreply.github.com>
…ackage is unresolvable Gating the warning on platform and arch alone produces a false positive. WebContainers and StackBlitz report linux/x64 and install @nx/nx-linux-x64-gnu normally, but dlopen fails there, so nx correctly runs WASM. Every StackBlitz boot printed a broken lockfile diagnosis that did not apply, and nx report called the runtime a fallback for the same reason. Gate both surfaces on the signature of the install this actually targets: the expected native package cannot be resolved at all. A package that resolves but will not load is left alone, as is NAPI_RS_FORCE_WASI and any platform outside the prebuilt matrix. Only a MODULE_NOT_FOUND counts as absent, so any other resolution error is treated as present and stays quiet. The expected package name and the resolvability probe are shared between the startup warning and nx report so the two cannot disagree. The report field is now a neutral native or wasm, and only appends (missing <package>) under that same predicate.
There was a problem hiding this comment.
Nx Cloud is proposing a fix for your failed CI:
We formatted astro-docs/src/content/docs/reference/environment-variables.mdoc to fix the astro-docs:format prettier check failure. The two new table rows added by this PR (NX_ALLOW_WASM_FALLBACK and NX_WASM_FALLBACK_WARNED) had descriptions exceeding the existing column width, causing prettier to reject the misaligned table. Running prettier --write on the file expanded the column separators to match the widest cell, bringing the file back into compliance.
Warning
- ❌ We could not verify this fix.
- The suggested diff is too large to display here, but you can view it on Nx Cloud ↗
🔔 Heads up, your workspace has pending recommendations ↗ to auto-apply fixes for similar failures.
Or Apply changes locally with:
npx nx-cloud apply-locally GMuW-AyST
Apply fix locally with your editor ↗ View interactive diff ↗
🎓 Learn more about Self-Healing CI on nx.dev
Current Behavior
When the platform-native binary is missing from
node_modules, nx silently falls back to its bundled WebAssembly runtime:native-bindings.jscollects native load errors into an array and quietly loadsnx.wasi.cjs, which ships inside thenxpackage, so the fallback always succeeds.isCI() || isDocker(), orNX_DAEMON=false), so the warning cannot fire in exactly the environment where this failure mode occurs.src/native/index.jssuppresses node's own WASIExperimentalWarning.The common trigger is a lockfile that lost its platform
optionalDependencies(regenerated on a different OS, npm/cli#4828, or a merge conflict). Real-world impact: an enterprise customer'spackage-lock.jsoncontained only@nx/nx-darwin-arm64, sonpm cion Linux runners installed no native binding.nx-cloud start-ci-runspun at 100% CPU for up to 2.5 hours per job with zero output even underNX_VERBOSE_LOGGING=true, and the diagnosis took several days because nothing pointed at the install.Expected Behavior
WASM is only treated as a fallback when the expected native package is unresolvable, which is the broken-install signature a pruned lockfile produces. Under that predicate, nx prints one loud, actionable warning to stderr naming the exact missing package and the fix:
src/native/wasm-fallback-warning.js(pure and unit-tested) computes the message fromplatform/arch/libc.MODULE_NOT_FOUND), not on platform inference. Environments where the package is installed but cannot be loaded, such as WebContainers/StackBlitz (which reportlinux/x64yet cannot dlopen native addons), stay quiet, as do platforms without prebuilt natives,NAPI_RS_FORCE_WASI, andNX_ALLOW_WASM_FALLBACK=true.src/native/index.jsemits it with a synchronousfs.writeSyncto stderr. A sync write is load-bearing here: async output is lost once the process blocks in synchronous WASM execution, which is why the customer saw nothing.NX_WASM_FALLBACK_WARNEDenv var dedupe), since nx spawns many child processes.nx reportnow includes a neutralNative runtimefield (nativeorwasm), appending(missing @nx/nx-<platform>)only under the same unresolvable-package predicate, so both surfaces share one definition of "broken install".ExperimentalWarningsuppression is intentionally kept: one clear deduped warning is more useful than N cryptic node warnings.project.jsonoutput globs were adjusted so the new checked-in helper is not matched by the napi build's output glob and clobbered by cache restores.Proposed follow-up for discussion (input wanted)
isCI()is true, throw an actionable error unlessNX_ALLOW_WASM_FALLBACK=trueis set. Rationale: a missing platform native in CI is almost always a broken install, and failing in seconds with the package name beats a multi-hour silent hang. Open questions: is warn-locally/error-in-CI the right split, and isNX_ALLOW_WASM_FALLBACKthe right escape-hatch name?Related Issue(s)
N/A (from an enterprise support incident; context in the linked Polygraph session)