Skip to content

Bun compatibility: top-level node:sea import in index.js and sdk/index.js fails resolve step #2894

@truffle-dev

Description

@truffle-dev

Describe the bug

@github/copilot@1.0.34 has unconditional top-level imports of node:sea in both of its entrypoint files:

  • index.js:7 is the CLI main entry, used when npm-loader.js falls back to import("./index.js") (the non-SEA install path).
  • sdk/index.js:~4123 is exposed via the "./sdk" field in exports. That subpath is the resolve target @github/copilot-sdk's getBundledCliPath() uses to locate the CLI.

Both imports serve a single purpose: X.isSea() is called by the auto-update logic (ELt() in sdk/index.js, similar in index.js) to short-circuit with "Update not supported when running js directly" when the CLI is not running from a Single Executable Application.

Because the import is top-level, non-Node runtimes that don't implement node:sea fail at resolve time, before any code runs. A try/catch around the import does not help: this is a module-graph error, not a runtime exception. Bun's current nodejs-compat doc does not list node:sea (SEA is a Node-specific packaging API, so this is expected).

Affected version

@github/copilot@1.0.34 (latest as of 2026-04-22).

Reproduced on: Bun 1.3.13 Linux x64, Node subprocess chain working normally but index.js loaded under Bun.

Steps to reproduce

Minimum:

mkdir bun-copilot-repro && cd bun-copilot-repro
bun init -y
bun add @github/copilot
bun -e 'await import("./node_modules/@github/copilot/index.js")'

Output:

error: No such built-in module: node:sea
Bun v1.3.13 (Linux x64)

User-facing symptom from the wild: cassidoo/emoji-list-generator#1. Reporter runs bun start on a TUI project that depends on @github/copilot-sdk. When the CLI subprocess is invoked, stderr shows:

error: Could not resolve: "node:sea". Maybe you need to "bun install"?
    at .../node_modules/@github/copilot/index.js:7:390

The SDK already contains a Bun workaround: getNodeExecPath() in @github/copilot-sdk's client.js returns "node" (as a PATH lookup) when process.versions.bun is set, so it tries to spawn actual Node. This works on systems where node in PATH really is Node. It breaks when node is aliased or shimmed to bun, which is common on Bun-first Mac setups, and leaves the CLI's index.js being parsed by Bun at the node:sea import.

Expected behavior

The node:sea import should not prevent the module from being resolved on runtimes where node:sea is not implemented. Under those runtimes, isSea() should return false (SEA is Node-only by definition) and the auto-updater should take its existing "not running SEA" branch.

Suggested fix

Three shapes, least invasive first:

  1. Lazy createRequire. Replace the top-level import with a function-scoped resolver:

    import { createRequire } from "node:module";
    function getSeaModule() {
      try { return createRequire(import.meta.url)("node:sea"); }
      catch { return null; }
    }
    function isSea() { return getSeaModule()?.isSea() ?? false; }

    node:module resolves fine on Bun; the inner require returns null on runtimes where node:sea isn't available.

  2. Dynamic import inside the checker. async function isSea() { try { const sea = await import("node:sea"); return sea.isSea(); } catch { return false; } }. More invasive because callers become async.

  3. Top-level runtime guard. if (!globalThis.Bun && !process.versions.bun) { /* import node:sea here */ } with conditional module-scope binding. Reads cleaner at the top of the file but doesn't cover hypothetical future non-Node runtimes that also lack SEA.

Option (1) is the smallest change and matches the createRequire pattern already used for vendored native modules (sharp, clipboard) in sdk/index.js.

Additional context

  • Adjacent Bun-compat rough edge in the same bundle (separate issue, not covered by the above fix): the bundled undici feature-detection at sdk/index.js:~211 wraps require("node:sqlite") in a try/catch that only recognizes ERR_UNKNOWN_BUILTIN_MODULE / ERR_NO_CRYPTO. Bun throws a different error shape ("No such built-in module") without those codes, so the catch doesn't degrade gracefully. Loading sdk/index.js under Bun fails on the node:sqlite layer first; fixing node:sea alone would surface the node:sqlite error next. Worth a second issue against undici upstream if the scope here is CLI-only.
  • Reference pattern in the wild: mcp-use/mcp-use#1382 applies the globalThis.Bun || process.versions.bun two-check guard for a different Node-only dependency (tsx/esm/api loader hooks); same shape, different builtin.
  • OS: macOS arm64 (reporter) / Linux x86_64 (my repro). Both hit the same index.js:7 failure.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:installationInstalling, updating, versioning, PATH setup, and binary distribution

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions