evanw/esbuild · error · Error
The "buildSync" API only works in node
Error message
The "buildSync" API only works in node
What it means
esbuild's browser build (`lib/npm/browser.ts`) exports `buildSync` as a function that always throws. The synchronous API exists only on the Node entry point because it relies on `child_process.execFileSync` to run the native binary and block the event loop, which is impossible in a browser main thread. Importing `esbuild` and calling `buildSync` means you are resolving the browser build.
Source
Thrown at lib/npm/browser.ts:33
export let version = ESBUILD_VERSION
export let build: typeof types.build = (options: types.BuildOptions) =>
ensureServiceIsRunning().build(options)
export let context: typeof types.context = (options: types.BuildOptions) =>
ensureServiceIsRunning().context(options)
export const transform: typeof types.transform = (input: string | Uint8Array, options?: types.TransformOptions) =>
ensureServiceIsRunning().transform(input, options)
export const formatMessages: typeof types.formatMessages = (messages, options) =>
ensureServiceIsRunning().formatMessages(messages, options)
export const analyzeMetafile: typeof types.analyzeMetafile = (metafile, options) =>
ensureServiceIsRunning().analyzeMetafile(metafile, options)
export const buildSync: typeof types.buildSync = () => {
throw new Error(`The "buildSync" API only works in node`)
}
export const transformSync: typeof types.transformSync = () => {
throw new Error(`The "transformSync" API only works in node`)
}
export const formatMessagesSync: typeof types.formatMessagesSync = () => {
throw new Error(`The "formatMessagesSync" API only works in node`)
}
export const analyzeMetafileSync: typeof types.analyzeMetafileSync = () => {
throw new Error(`The "analyzeMetafileSync" API only works in node`)
}
export const stop = () => {
if (stopService) stopService()
return Promise.resolve()
}View on GitHub (pinned to 6ff1d8b0d8)
Solutions
- Switch to the async `esbuild.build()` (returns a Promise) which is supported in the browser via the wasm service.
- Ensure your bundler does not rewrite esbuild to its browser build — mark esbuild as external if you intend to use it only in Node.
- If you genuinely need esbuild in the browser, use the `esbuild-wasm` package and call `initialize({ wasmURL })` before async APIs.
- Move the `buildSync` call to a Node-only entry point that is never loaded in the browser bundle.
Example fix
// before
const result = esbuild.buildSync({ entryPoints: ['a.ts'], bundle: true })
// after
const result = await esbuild.build({ entryPoints: ['a.ts'], bundle: true }) Defensive patterns
Strategy: validation
Validate before calling
const isBrowser = typeof window !== 'undefined' || typeof self !== 'undefined'
if (isBrowser && typeof (esbuild as any).buildSync === 'function') {
// will throw — switch to async build
}
// Prefer: detect before invoking
function safeBuild(opts) {
return isBrowser ? esbuild.build(opts) : Promise.resolve(esbuild.buildSync(opts))
} Type guard
function supportsSyncApi(): boolean {
return typeof process !== 'undefined' && !!process.versions?.node && typeof window === 'undefined'
} Try / catch
try {
const r = esbuild.buildSync(opts)
} catch (e) {
if (/only works in node/.test(String((e as Error).message))) {
return await esbuild.build(opts) // graceful async fallback
}
throw e
} Prevention
- Mark esbuild external in browser-targeted bundlers.
- Prefer async esbuild.build() everywhere for cross-runtime portability.
- Gate sync calls behind a Node environment check.
- Document which entry points are Node-only.
When it happens
Trigger: Calling `esbuild.buildSync(options)` after `esbuild` has resolved to `lib/npm/browser.ts` — typically because a bundler (webpack/vite/esbuild itself) targeted the browser field, or because code ran in a browser/worker context.
Common situations: Using esbuild inside a browser-targeted webpack/vite build; importing esbuild in an isomorphic code path that runs in the browser; tooling that auto-selects the `browser` field of package.json; running esbuild in a Cloudflare Worker or Deno browser-like environment.
Related errors
- The "transformSync" API only works in node
- The "formatMessagesSync" API only works in node
- The "analyzeMetafileSync" API only works in node
- Must provide either the "wasmURL" option or the "wasmModule"
- You need to wait for the promise returned from "initialize"
AI-assisted analysis of evanw/esbuild@6ff1d8b0d8 (2026-08-03).
Data as JSON: /data/errors/06bc8c354ccf15b7.json.
Report an issue: GitHub.