evanw/esbuild · error · Error

The "formatMessagesSync" API only works in node

Error message

The "formatMessagesSync" API only works in node

What it means

The browser entry of esbuild (lib/npm/browser.ts) defines formatMessagesSync as a stub that always throws. Since the browser build communicates with a WebAssembly service asynchronously, no synchronous formatting API is possible.

Solutions

  1. Switch to esbuild.formatMessages(...) and await the result.
  2. Run under the native esbuild package in node if the synchronous variant is needed.
  3. Cache formatted messages from an async pre-pass instead of formatting synchronously at render time.

Example fix

// before
const formatted = esbuild.formatMessagesSync(msgs, { kind: 'error' })

// after
const formatted = await esbuild.formatMessages(msgs, { kind: 'error' })
Defensive patterns

Strategy: validation

Validate before calling

// Use async formatting in environments that may be browser-based.
const isBrowserBuild =
  typeof window !== 'undefined' && typeof window.document !== 'undefined'

const formatted = isBrowserBuild
  ? await esbuild.formatMessages(msgs, { kind: 'error' })
  : esbuild.formatMessagesSync(msgs, { kind: 'error' })

Prevention

When it happens

Trigger: Application code calls esbuild.formatMessagesSync(messages, options) while running against the browser build of esbuild (esbuild-wasm or the 'browser' field).

Common situations: Error-formatting helpers copied from a node setup into a browser context; diagnostic tooling shared between node CLI and web UI.

Related errors


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

Appendix: source

Thrown at lib/npm/browser.ts:41

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()
}

interface Service {
  build: typeof types.build
  context: typeof types.context
  transform: typeof types.transform
  formatMessages: typeof types.formatMessages
  analyzeMetafile: typeof types.analyzeMetafile
}

View on GitHub (pinned to f6058f8364)