[needs-vps] fix(electron): strip hashed native modules from standalone and fix removal path - #7123
Conversation
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
|
Thanks for the clean root-cause writeup — the diagnosis (hashed native-module copies under the wrong |
|
Additional field confirmation for this PR, plus a suggested regression guard. Confirmation — Discussion #7144 (@agd-stack, Windows 11 x64) isolated the trigger with a clean A/B on v3.8.48: clean DATA_DIR → Suggested regression guard. process.env.ELECTRON_SMOKE_DATA_DIR ||
(await mkdtemp(join(tmpdir(), "omniroute-electron-smoke-")));The failing code path is the existing-DB probe branch in The script already supports an Worth noting the smoke step is Not blocking the merge — this PR addresses the trigger correctly. Flagging the coverage gap so the fix does not silently regress later. Context: #7132, #7144, and the split-out fallback-ordering follow-up in #7288. |
|
This PR is broader than its title suggests, and a new macOS report (#7346 / discussion #7345) confirms it. Flagging because the It is not a native-module fix — it is a packaging fix for every externalized package. @jrickybt's macOS report enumerates 14 hash-mangled externals that all fail to resolve in the packaged app, of which only three are native:
It also fixes macOS, despite being verified only on Windows. Residual gap worth a second look before merge. Most of the 14 are in Suggested regression guard (the "how did this ship" half). It exists only as the manual Not blocking: the fix itself is correct and I would land it. Flagging the scope so it gets prioritized as the cross-platform release-blocker it is, rather than a Windows-only nice-to-have. |
|
Two things worth knowing before this merges — a sibling PR on the same root cause, and why this one has no real CI signal. 1. #6794 attacks the same bug from the opposite direction. #6794 (
Zero files in common, so both merge clean and go green independently, and the runtime outcome would be settled by whichever executes first rather than by a decision. Flagging rather than resolving — the pick is yours. My read: they are complementary (strip the require and leave no dangling links to shadow resolution), but only this PR is currently live; #6794's helper is orphaned as submitted (I have said the same over there). One concrete conflict to settle either way: keytar. This PR adds it to the 2. #6794 has the better root-cause evidence, and it resolves my earlier caveat. Its author captured the actual packaged app: The hashed entries are not missing — they are dangling absolute symlinks into the CI runner. That explains @jrickybt's macOS 3. Why this PR looks stuck — it is not the The base has moved 480 commits since this PR's merge-base, so the full suite (unit, build, docs gates, quality) never ran against it. A Net: this is the PR that actually fixes the packaged app today, and it fixes the whole externalized-module class (not just the natives) across mac/win/linux — see my earlier comment. But it should not be judged against #6794 on CI colour until its base is refreshed. |
ec56dfd to
4a91ac3
Compare
… (queue_conditions alone are eligibility-only) (diegosouzapw#7179)
…uto_merge_conditions (rules-based path is EOL 2026-07-16) (diegosouzapw#7216)
…; free plan queue is serial) (diegosouzapw#7220)
…H-hosted build hang dequeued every attempt) (diegosouzapw#7225)
…moval path
Two issues caused better-sqlite3 (and other native modules) to fail
with NODE_MODULE_VERSION mismatch in the Electron desktop app:
1. removeNativeModules targeted '.next' instead of the actual distDir
('.build/next'), so Next.js-traced native modules with mangled names
(better-sqlite3-{hash}, keytar-{hash}, sqlite-vec-{hash}) were never
removed from the standalone bundle.
2. patchTurbopackChunks was not enabled for the Electron build, so
server chunks still referenced the mangled module names. The server
loaded the stale Node-ABI .node from the mangled copy, while the
correctly rebuilt Electron-ABI copy sat unused.
Changes:
- Enable patchTurbopackChunks in assembleStandalone call (strips hash
suffixes from require() calls in server chunks)
- Fix removeNativeModules path: '.next' -> NEXT_DIST_DIR
- Add sqlite-vec to the native module removal list
Fixes: diegosouzapw#7082
Extract removeNativeModules() into a side-effect-free module
(scripts/build/lib/removeNativeModules.mjs) so it can be unit-tested
without importing the full Electron build harness.
Add 6 tests covering:
- no-op when baseDir does not exist / is empty
- removal of hashed native modules (better-sqlite3-{hash}, keytar-{hash})
- removal of unhashed native modules matching prefixes
- default prefixes only remove keytar
- preservation of non-matching modules (prefix not at start)
Also clean up unused readdirSync import from prepare-electron-standalone.mjs.
….mergify.yml scope creep (diegosouzapw#7123) The extract-removeNativeModules refactor dropped the buildRebuildSpawnPlan import (still used at rebuildBetterSqlite3ForElectron), which would crash the Electron packaging step with ReferenceError. Restore it. Also revert the unrelated .mergify.yml merge-queue-config changes that had crept into this Electron packaging PR (release-infra config, out of scope). Rebased onto release/v3.8.49 so diegosouzapw#6794's materializeSymlinks:true and this PR's patchTurbopackChunks:true both remain in the assembleStandalone() call.
4a91ac3 to
cd6140a
Compare
|
Maintainer fix-in-place pass (rebased onto
Validation: the extracted |
|
Thank you for digging into the Electron better-sqlite3 ABI mismatch — that's a genuinely nasty, hard-to-diagnose class of bug, and the analysis in your PR was sharp. 🙏 I'm closing it as subsumed, and I want to show the proof because your diagnosis was actually correct — the root cause just got covered by two other merges since I first reviewed this. Both mechanisms you propose are now already on So the bug you targeted should now be fixed on the release branch. If you build from the latest release and still hit an ABI mismatch, please reopen or file an issue with the exact error — I'd want to know. Really appreciate the careful work here; please keep contributing! 🚀 |
removeNativeModules() was called with a hardcoded ".next" path while the actual
distDir is NEXT_DIST_DIR (".build/next" by default). Because the function
early-returns when the directory does not exist, the cleanup silently no-opped
and the plain-Node-ABI better-sqlite3 copy produced by `next build` survived
into the packaged app.
At runtime the standalone server runs under ELECTRON_RUN_AS_NODE, so it needs
the Electron ABI (148 for electron 43). Loading the ABI-137 copy fails with
ERR_DLOPEN_FAILED, the app falls back to the sql.js WASM driver, the connection
is closed and retried in a loop, WASM memory is never reclaimed and the process
OOMs -> HTTP 500 on every route.
Also adds assertNoStaleHashedNatives() so a wrong baseDir fails the build
instead of silently shipping a broken installer. This has regressed at least
twice (diegosouzapw#1497 with ABI 127 vs 145, diegosouzapw#7082/diegosouzapw#7681 with 137 vs 148).
Refs diegosouzapw#7082, diegosouzapw#7681, diegosouzapw#1497, diegosouzapw#8792. Supersedes the abandoned diegosouzapw#7123.
…8794) removeNativeModules() was called with a hardcoded ".next" path while the actual distDir is NEXT_DIST_DIR (".build/next" by default). Because the function early-returns when the directory does not exist, the cleanup silently no-opped and the plain-Node-ABI better-sqlite3 copy produced by `next build` survived into the packaged app. At runtime the standalone server runs under ELECTRON_RUN_AS_NODE, so it needs the Electron ABI (148 for electron 43). Loading the ABI-137 copy fails with ERR_DLOPEN_FAILED, the app falls back to the sql.js WASM driver, the connection is closed and retried in a loop, WASM memory is never reclaimed and the process OOMs -> HTTP 500 on every route. Also adds assertNoStaleHashedNatives() so a wrong baseDir fails the build instead of silently shipping a broken installer. This has regressed at least twice (#1497 with ABI 127 vs 145, #7082/#7681 with 137 vs 148). Refs #7082, #7681, #1497, #8792. Supersedes the abandoned #7123.
…iegosouzapw#8794) removeNativeModules() was called with a hardcoded ".next" path while the actual distDir is NEXT_DIST_DIR (".build/next" by default). Because the function early-returns when the directory does not exist, the cleanup silently no-opped and the plain-Node-ABI better-sqlite3 copy produced by `next build` survived into the packaged app. At runtime the standalone server runs under ELECTRON_RUN_AS_NODE, so it needs the Electron ABI (148 for electron 43). Loading the ABI-137 copy fails with ERR_DLOPEN_FAILED, the app falls back to the sql.js WASM driver, the connection is closed and retried in a loop, WASM memory is never reclaimed and the process OOMs -> HTTP 500 on every route. Also adds assertNoStaleHashedNatives() so a wrong baseDir fails the build instead of silently shipping a broken installer. This has regressed at least twice (diegosouzapw#1497 with ABI 127 vs 145, diegosouzapw#7082/diegosouzapw#7681 with 137 vs 148). Refs diegosouzapw#7082, diegosouzapw#7681, diegosouzapw#1497, diegosouzapw#8792. Supersedes the abandoned diegosouzapw#7123.
Problem
Electron desktop app (3.8.45–3.8.48) fails with
Internal Server Errorbecausebetter-sqlite3cannot load — the server falls back tosql.jsWASM which OOMs on non-trivial databases.Root cause: Two issues in the Electron build pipeline leave stale Node-ABI native modules in the standalone bundle, shadowing the correctly rebuilt Electron-ABI copies.
Details
removeNativeModulestargets wrong path: Uses.nextinstead of the actualdistDir(.build/next), so Next.js-traced native modules with mangled names (better-sqlite3-{hash},keytar-{hash},sqlite-vec-{hash}) are never removed.patchTurbopackChunksnot enabled: Without it, server chunks still reference the mangled module names (e.g.require('better-sqlite3-90e2652d1716b047')). The server loads the stale Node-ABI.nodefrom the mangled copy, while the correctly rebuilt Electron-ABI copy atnode_modules/better-sqlite3/sits unused.Changes
patchTurbopackChunks: truein theassembleStandalonecall (strips hash suffixes fromrequire()calls in server chunks)removeNativeModulespath:.next→NEXT_DIST_DIRsqlite-vecto the native module removal listVerification
The fix was manually verified on Windows 11 with OmniRoute 3.8.48 by:
better_sqlite3.nodeinto the mangled module path — server started successfullyCloses #7082