evanw/esbuild · error · Error
The "buildSync" API only works in node
Error message
The "buildSync" API only works in node
What it means
The browser entry of esbuild (lib/npm/browser.ts) defines buildSync as a function that unconditionally throws, because the browser build is backed by WebAssembly which is inherently asynchronous and cannot perform a synchronous build. esbuild ships this stub so that code importing the browser variant still sees the buildSync export (preserving the type surface) but fails loudly instead of silently doing nothing.
Solutions
- Replace the synchronous call with the asynchronous esbuild.build(...) and await its result.
- If you genuinely need the native binary, import the node entry (esbuild, not esbuild-wasm) and run under node.
- Refactor shared code to be async so it works in both node and browser.
Example fix
// before
const result = esbuild.buildSync({ entryPoints: ['app.ts'], bundle: true })
// after
const result = await esbuild.build({ entryPoints: ['app.ts'], bundle: true }) Defensive patterns
Strategy: validation
Validate before calling
// Detect the browser build and avoid the sync stub.
const isBrowserBuild =
typeof window !== 'undefined' && typeof window.document !== 'undefined'
if (isBrowserBuild) {
// use async build
const result = await esbuild.build(options)
} else {
const result = esbuild.buildSync(options)
} Prevention
- Prefer the asynchronous API everywhere; it works in both node and browser.
- Keep esbuild usage async in any code that might run in a browser or WASM context.
- When sharing modules between node and browser, never rely on the *Sync APIs.
When it happens
Trigger: A module resolver / bundler selects lib/npm/browser.ts (via the package.json 'browser' field or because the environment is detected as a browser) and application code then calls esbuild.buildSync(options).
Common situations: Node-targeted code that calls buildSync gets reused or bundled into a browser build; using esbuild-wasm with the same API calls as native esbuild; test files run under a browser test runner.
Related errors
- The "analyzeMetafileSync" API only works in node
- The "formatMessagesSync" API only works in node
- The "transformSync" API only works in node
- You need to wait for the promise returned from "initialize"…
- Cannot use the "serve" API in this environment
AI-assisted analysis of evanw/esbuild@f6058f8364 (2026-08-09).
Data as JSON: /api/errors/06bc8c354ccf15b7.
Report an issue: GitHub.
Appendix: 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 f6058f8364)