Skip to content

fix(sea): patch process.dlopen in worker threads - #297

Merged
robertsLando merged 1 commit into
mainfrom
fix/sea-worker-dlopen
Aug 25, 2026
Merged

fix(sea): patch process.dlopen in worker threads#297
robertsLando merged 1 commit into
mainfrom
fix/sea-worker-dlopen

Conversation

@robertsLando

Copy link
Copy Markdown
Member

Closes #293

Problem

Native addons fail to load from worker threads in SEA mode:

Error: The specified module could not be found.
\\?\C:\snapshot\test\node_modules\roaring\native\roaring-node-v137-win32-x64-unknown\roaring.node
  code: 'ERR_DLOPEN_FAILED'

The same code works in classic (non-SEA) pkg.

Cause

patchDlopen is what extracts a .node out of the snapshot into ~/.cache/pkg/<hash> before handing a real filesystem path to the loader. prelude/sea-bootstrap-core.js calls it once, on the main thread.

Each worker thread gets its own process object, so that patch does not carry over:

// node -e, plain Worker with eval
worker sees patched dlopen? false

prelude/sea-worker-entry.js already reapplies setupProcessPkg and patchIntlSegmenter per thread for exactly this reason — patchDlopen was just missing from the list. Workers therefore got the raw C:\snapshot\... path straight through to dlopen.

Classic pkg is unaffected because prelude/bootstrap.js runs in every thread and patches there.

Fix

One line in prelude/sea-worker-entry.js:

shared.patchDlopen(vfs.insideSnapshot);

Same fix the reporter verified in the issue.

Test

Extended test-90-sea-worker-threads rather than adding a second SEA build (each postject run costs several seconds in CI). The worker now requires a .node from the snapshot and reports whether it was extracted.

The fixture is not a real shared library, so dlopen throws in both cases — the discriminator is whether extraction happened, which only occurs when process.dlopen is patched in that thread. index.js points PKG_NATIVE_CACHE_PATH at a temp dir so nothing lands in the real ~/.cache, and main.js cleans it up.

Verified both directions on linux/node22:

  • with the fix: addonExtracted:true
  • with the fix reverted: addonExtracted:false, test fails

Note, not fixed here

patchChildProcess has the same shape — sea-bootstrap-core.js calls it on the main thread only, and sea-worker-entry.js does not reapply it, while classic pkg patches it in every thread. Nobody has reported it, and it needs its own test, so I left it out of this PR.

https://claude.ai/code/session_01PUj6PfkgQHcCg2bfXucBzk

Native addons failed to load from SEA worker threads with
ERR_DLOPEN_FAILED on the raw C:\snapshot path.

Each worker thread gets its own `process` object, so the
`shared.patchDlopen()` call in sea-bootstrap-core only covers the main
thread. sea-worker-entry already reapplies `patchIntlSegmenter` for the
same reason but was missing `patchDlopen`, so workers never got the
extract-to-cache step that makes addons loadable. Classic (non-SEA) pkg
is unaffected: bootstrap.js runs in every thread and patches there.

test-90-sea-worker-threads now loads a `.node` from the snapshot inside
the worker and asserts it was extracted to the native cache. The fake
addon is not a real shared library so dlopen throws either way — the
extraction is what distinguishes patched from unpatched.

Closes #293

Claude-Session: https://claude.ai/code/session_01PUj6PfkgQHcCg2bfXucBzk
@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.23%. Comparing base (c1e10f5) to head (43b6220).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #297      +/-   ##
==========================================
+ Coverage   87.19%   87.23%   +0.03%     
==========================================
  Files          23       23              
  Lines        7929     7929              
  Branches     1214     1214              
==========================================
+ Hits         6914     6917       +3     
+ Misses       1008     1005       -3     
  Partials        7        7              

see 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@robertsLando
robertsLando merged commit fd33206 into main Aug 25, 2026
28 checks passed
@robertsLando
robertsLando deleted the fix/sea-worker-dlopen branch August 25, 2026 06:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Native modules don't work in worker threads using sea

1 participant