evanw/esbuild · error · Error

The "wasmURL" option only works in the browser

Error message

The "wasmURL" option only works in the browser

What it means

In the node entry, initialize() (lib/npm/node.ts:232) validates options and explicitly rejects wasmURL, throwing because node does not use WebAssembly for esbuild (it spawns the native binary directly). wasmURL is a browser-only option; passing it in node indicates a config meant for esbuild-wasm/esbuild browser being reused under native esbuild.

Solutions

  1. Remove wasmURL from the options passed to initialize in node (node does not need or accept it).
  2. Omit the initialize() call entirely in node; the service auto-starts on first use.
  3. If you truly need WebAssembly in node, use the esbuild-wasm package instead of esbuild.

Example fix

// before
import * as esbuild from 'esbuild'          // native node build
await esbuild.initialize({ wasmURL: '/esbuild.wasm' })  // throws

// after
import * as esbuild from 'esbuild'
await esbuild.build({ entryPoints: ['a.ts'] })  // auto-starts, no initialize needed
Defensive patterns

Strategy: validation

Validate before calling

// Strip browser-only options before calling initialize in node.
function nodeInitOptions(opts) {
  const { wasmURL: _drop1, wasmModule: _drop2, worker: _drop3, ...rest } = opts || {}
  return rest
}
// In node you usually skip initialize() entirely; the service auto-starts.

Type guard

// Ensure an options object is free of browser-only keys for node.
function hasNoBrowserOnlyOptions(o: any): o is Record<string, never> {
  return o.wasmURL === undefined && o.wasmModule === undefined && o.worker === undefined
}

Prevention

When it happens

Trigger: Calling esbuild.initialize({ wasmURL: '...' }) (or a shared config object containing wasmURL) while importing the native node esbuild package.

Common situations: Shared initialization helper used by both browser (esbuild-wasm) and node (esbuild) code paths that unconditionally passes browser options; copy-pasting a browser example into node.

Related errors


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

Appendix: source

Thrown at lib/npm/node.ts:234

    callName: 'analyzeMetafileSync',
    refs: null,
    metafile: typeof metafile === 'string' ? metafile : JSON.stringify(metafile),
    options,
    callback: (err, res) => { if (err) throw err; result = res! },
  }))
  return result!
}

export const stop = async () => {
  if (stopService) await stopService()
  if (workerThreadService) workerThreadService.stop()
}

let initializeWasCalled = false

export let initialize: typeof types.initialize = options => {
  options = common.validateInitializeOptions(options || {})
  if (options.wasmURL) throw new Error(`The "wasmURL" option only works in the browser`)
  if (options.wasmModule) throw new Error(`The "wasmModule" option only works in the browser`)
  if (options.worker) throw new Error(`The "worker" option only works in the browser`)
  if (initializeWasCalled) throw new Error('Cannot call "initialize" more than once')
  ensureServiceIsRunning()
  initializeWasCalled = true
  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
}

let defaultWD = process.cwd()
let longLivedService: Service | undefined

View on GitHub (pinned to f6058f8364)