evanw/esbuild · error · Error

The esbuild JavaScript API cannot be bundled. Please mark…

Error message

The esbuild JavaScript API cannot be bundled. Please mark the "esbuild" package as external so it's not included in the bundle.

More information: The file containing the code for esbuild's JavaScript API (${__filename}) does not appear to be inside the esbuild package on the file system, which usually means that the esbuild package was bundled into another file. This is problematic because the API needs to run a binary executable inside the esbuild package which is located using a relative path from the API code to the executable. If the esbuild package is bundled, the relative path will be incorrect and the executable won't be found.

What it means

esbuildCommandAndArgs (lib/npm/node.ts:51) checks whether the currently-executing file looks like it still lives inside the esbuild package (basename 'main.js' inside a dir named 'lib'). If not, it concludes the esbuild JS API was bundled into another file by a tool like webpack/esbuild/rollup. This is fatal because esbuild locates its native binary via a path relative to the API source file; once bundled, that relative path is wrong and the binary cannot be found. esbuild deliberately fails early with an explanatory message rather than a cryptic ENOENT later.

Solutions

  1. Mark 'esbuild' (and '@esbuild/*') as external in your bundler config so it is required at runtime from node_modules.
  2. Require/import esbuild lazily at runtime instead of bundling it.
  3. If you must relocate the binary, set ESBUILD_BINARY_PATH to an absolute path to the native binary.

Example fix

// before (esbuild bundling a tool that itself uses esbuild)
build({ entryPoints: ['src/cli.ts'], bundle: true, outfile: 'dist/cli.js' })

// after
build({
  entryPoints: ['src/cli.ts'],
  bundle: true,
  outfile: 'dist/cli.js',
  external: ['esbuild'],
})
Defensive patterns

Strategy: validation

Validate before calling

// Fail the bundler build early if esbuild is not marked external.
const requiredExternals = ['esbuild']
const actualExternals = config.external || []
const missing = requiredExternals.filter(e => !actualExternals.includes(e))
if (missing.length) {
  throw new Error(`Mark these as external to avoid bundling esbuild: ${missing.join(', ')}`)
}

Prevention

When it happens

Trigger: Bundling a tool/library that imports esbuild (so lib/npm/node.ts gets inlined into a dist bundle) without marking 'esbuild' as external, then running the bundle, which calls build/transform and hits esbuildCommandAndArgs.

Common situations: Authoring a CLI that wraps esbuild and shipping a bundled dist; serverless framework bundling that includes esbuild; esbuild used inside another bundler's plugin without externalization; monorepo build pipelines that pre-bundle tooling.

Related errors


AI-assisted analysis of evanw/esbuild@f6058f8364 (2026-08-09). Data as JSON: /api/errors/31c34a781859af71. Report an issue: GitHub.

Appendix: source

Thrown at lib/npm/node.ts:55

    +major < 12 || (+major === 12 && +minor < 17)

    // >=v13.0.0 && <v13.13.0 also does not work
    || (+major === 13 && +minor < 13)
  ) {
    worker_threads = void 0
  }
}

// This should only be true if this is our internal worker thread. We want this
// library to be usable from other people's worker threads, so we should not be
// checking for "isMainThread".
let isInternalWorkerThread = worker_threads?.workerData?.esbuildVersion === ESBUILD_VERSION

let esbuildCommandAndArgs = (): [string, string[]] => {
  // Try to have a nice error message when people accidentally bundle esbuild
  // without providing an explicit path to the binary, or when using WebAssembly.
  if ((!ESBUILD_BINARY_PATH || WASM) && (path.basename(__filename) !== 'main.js' || path.basename(__dirname) !== 'lib')) {
    throw new Error(
      `The esbuild JavaScript API cannot be bundled. Please mark the "esbuild" ` +
      `package as external so it's not included in the bundle.\n` +
      `\n` +
      `More information: The file containing the code for esbuild's JavaScript ` +
      `API (${__filename}) does not appear to be inside the esbuild package on ` +
      `the file system, which usually means that the esbuild package was bundled ` +
      `into another file. This is problematic because the API needs to run a ` +
      `binary executable inside the esbuild package which is located using a ` +
      `relative path from the API code to the executable. If the esbuild package ` +
      `is bundled, the relative path will be incorrect and the executable won't ` +
      `be found.`)
  }

  if (WASM) {
    return ['node', [path.join(__dirname, '..', 'bin', 'esbuild')]]
  } else {
    const { binPath, isWASM } = generateBinPath()
    if (isWASM) {

View on GitHub (pinned to f6058f8364)