Skip to content

fix: work around Bun/Windows UV_FS_O_FILEMAP incompatibility in tar#16853

Merged
Hona merged 1 commit intoanomalyco:refactor/lsp-core-improvementsfrom
Hona:fix/bun-windows-tar-filemap
Mar 10, 2026
Merged

fix: work around Bun/Windows UV_FS_O_FILEMAP incompatibility in tar#16853
Hona merged 1 commit intoanomalyco:refactor/lsp-core-improvementsfrom
Hona:fix/bun-windows-tar-filemap

Conversation

@Hona
Copy link
Copy Markdown
Member

@Hona Hona commented Mar 10, 2026

Summary

Fixes the Windows-only test failure in tool.registry > loads tools with external dependencies without crashing.

Root Cause

tar@7 uses UV_FS_O_FILEMAP (a libuv/Windows memory-mapped I/O flag) for fs.open() on all files < 512KB on Windows (get-write-flag.js). Bun does not support this flagfs.open() returns EINVAL. tar's error handler ([ONERROR] in unpack.js) treats this as a non-fatal warning and silently skips the file. The result: Arborist.reify() reports success, but node_modules contains only empty directory structures — no actual files.

Evidence (step-by-step isolation)

Step Test Node Bun
1 fs.openSync(path, UV_FS_O_FILEMAP | O_TRUNC | O_CREAT | O_WRONLY) works EINVAL
2 tar.x() with onwarn 0 warnings 202 EINVAL warnings
3 __FAKE_PLATFORM__=linux before require('tar') then tar.x() - 0 warnings, all files
4 Same env var before require('arborist') then arb.reify() - all files extracted
5 ESM: env var then import { Arborist } (static) - still broken (hoisted)
6 env var then await import("@npmcli/arborist") (dynamic) - works

Fix

  • Set process.env.__FAKE_PLATFORM__ = "linux" at module top level on Windows — makes tar use plain 'w' flag instead of UV_FS_O_FILEMAP
  • Change static import { Arborist } to dynamic await import() — ensures the env var is set before tar evaluates its cached flag at module load time
  • Remove stray console.log(result) from Npm.install()

Bun on Windows does not support the UV_FS_O_FILEMAP flag that tar@7
uses for files < 512KB (fs.open returns EINVAL). tar silently swallows
the error and skips writing files, leaving only empty directories in
node_modules.

Set __FAKE_PLATFORM__=linux before arborist loads so tar falls back to
the plain 'w' flag. Use dynamic import() for @npmcli/arborist to ensure
the env var is set before tar evaluates its cached flag at module load
time. Also removes stray console.log from Npm.install().
Copilot AI review requested due to automatic review settings March 10, 2026 05:50
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Works around a Windows-only Bun runtime incompatibility where tar@7 uses UV_FS_O_FILEMAP (unsupported by Bun) causing @npmcli/arborist installs to “succeed” while silently skipping file extraction.

Changes:

  • Sets process.env.__FAKE_PLATFORM__ = "linux" on Windows to force tar to avoid UV_FS_O_FILEMAP.
  • Replaces static @npmcli/arborist import with dynamic await import() so the env var is set before module evaluation.
  • Removes a stray console.log(result) from Npm.install().

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +63 to 64
const { Arborist } = await import("@npmcli/arborist")
const arborist = new Arborist({
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dynamic await import("@npmcli/arborist") here duplicates the same logic in install(). To keep the Bun/Windows workaround and import behavior consistent, consider extracting a shared helper (optionally caching the import promise) that returns Arborist and is reused by both call sites.

Copilot uses AI. Check for mistakes.
Comment on lines +98 to 100
const { Arborist } = await import("@npmcli/arborist")
const arb = new Arborist({
path: dir,
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: this second dynamic import is a separate spot that would need to stay in sync with the Bun/Windows workaround. Reusing a single getArborist() helper (and possibly caching the import) would reduce the chance of future drift.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +10
// Workaround: Bun on Windows does not support the UV_FS_O_FILEMAP flag that
// the `tar` package uses for files < 512KB (fs.open returns EINVAL).
// tar silently swallows the error and skips writing files, leaving only empty
// directories. Setting __FAKE_PLATFORM__ makes tar fall back to the plain 'w'
// flag. See tar's get-write-flag.js.
// Must be set before @npmcli/arborist is imported since tar caches the flag
// at module evaluation time — so we use a dynamic import() below.
if (process.platform === "win32") {
process.env.__FAKE_PLATFORM__ = "linux"
}
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sets process.env.__FAKE_PLATFORM__ as a module side-effect for every Windows run, but the workaround is described as Bun-on-Windows specific and it also globally “spoofs” the platform for any other dependency that might consult __FAKE_PLATFORM__. Consider scoping it to Bun (e.g. process.versions.bun) and setting/restoring the env var only immediately around the dynamic @npmcli/arborist import so the override doesn’t leak to the rest of the process.

Copilot uses AI. Check for mistakes.
@Hona Hona merged commit 8b5033b into anomalyco:refactor/lsp-core-improvements Mar 10, 2026
14 checks passed
@thdxr thdxr mentioned this pull request Mar 10, 2026
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.

2 participants