evanw/esbuild · critical · Error
The esbuild JavaScript API cannot be bundled. Please mark th
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` (`node.ts:54`) detects when esbuild's own JS API file is no longer located at `<pkg>/lib/main.js` — meaning a bundler has inlined esbuild into another file. Since esbuild locates its native binary via a relative path from its source file, bundling breaks the path lookup and the binary can't be found. The throw happens at first API use (e.g. `build`, `transform`).
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 6ff1d8b0d8)
Solutions
- Mark esbuild as external in your bundler config: webpack `externals`, rollup `external`, esbuild `external: ['esbuild']`, vite `build.rollupOptions.external`.
- Use the `ESBUILD_BINARY_PATH` env var to point esbuild at its native binary explicitly (this also short-circuits the bundle detection in `esbuildCommandAndArgs`).
- Don't bundle esbuild at all — leave it as a runtime dependency resolved from `node_modules`.
- If you must ship a single file, switch to `esbuild-wasm` and bundle the wasm separately.
Example fix
// before (esbuild bundling itself)
await esbuild.build({
entryPoints: ['src/cli.ts'],
bundle: true,
// esbuild gets inlined -> runtime error
})
// after
await esbuild.build({
entryPoints: ['src/cli.ts'],
bundle: true,
external: ['esbuild'],
}) Defensive patterns
Strategy: validation
Validate before calling
import { basename, dirname } from 'path'
function isEsbuildBundled(): boolean {
// Reuses esbuild's own heuristic: API file must live at <pkg>/lib/main.js
return !(basename(__filename) === 'main.js' && basename(dirname(__filename)) === 'lib')
} Prevention
- Always add 'esbuild' to your bundler's external list.
- If you must bundle, set ESBUILD_BINARY_PATH so esbuild skips path-based detection.
- Prefer esbuild-wasm (with separate wasm bundling) for fully self-contained bundles.
- Smoke-test the bundled output by invoking build/transform once in CI.
When it happens
Trigger: Bundling the `esbuild` package into a single file (webpack/vite/rollup/esbuild-itself) without marking it external, then importing the bundled result. The check `path.basename(__filename) !== 'main.js' || path.basename(__dirname) !== 'lib'` is true because the bundled file lives elsewhere.
Common situations: Authoring a CLI or library that uses esbuild and shipping it as a bundle without `external: ['esbuild']`; a serverless function bundler that includes all deps; a monorepo build pipeline that bundles shared tools; mistakenly running esbuild through itself for deployment.
Related errors
- The "buildSync" API only works in node
- Must provide either the "wasmURL" option or the "wasmModule"
- The "esbuild" package cannot be installed because ${os} is t
- Expected ${JSON.stringify(packageJSON.version)} but got ${JS
- Unsupported platform: ${platformKey}
AI-assisted analysis of evanw/esbuild@6ff1d8b0d8 (2026-08-03).
Data as JSON: /data/errors/31c34a781859af71.json.
Report an issue: GitHub.