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

  1. Replace the synchronous call with the asynchronous esbuild.build(...) and await its result.
  2. If you genuinely need the native binary, import the node entry (esbuild, not esbuild-wasm) and run under node.
  3. 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

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


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)