evanw/esbuild · error · Error

Must provide either the "wasmURL" option or the…

Error message

Must provide either the "wasmURL" option or the "wasmModule" option

What it means

The browser build's initialize() (lib/npm/browser.ts:76) requires the caller to explicitly tell esbuild where the WebAssembly module is, since a browser has no filesystem to locate it automatically. If neither wasmURL nor wasmModule is supplied, it throws. These options are mutually sufficient: provide at least one.

Solutions

  1. Provide wasmURL pointing to your served esbuild.wasm (absolute URL, relative URL, or CDN URL).
  2. Alternatively, pass a precompiled WebAssembly.Module via wasmModule (e.g. fetch+compile once and reuse).
  3. Ensure the wasmURL resolves to the same esbuild version as the JS package to avoid protocol/ABI mismatch.

Example fix

// before
await esbuild.initialize({})

// after
await esbuild.initialize({ wasmURL: '/node_modules/esbuild-wasm/esbuild.wasm' })
Defensive patterns

Strategy: validation

Validate before calling

// Validate required wasm options before calling initialize.
function makeInitOptions(wasmURL?: string, wasmModule?: WebAssembly.Module) {
  if (!wasmURL && !wasmModule) {
    throw new Error('Initialize requires wasmURL or wasmModule')
  }
  return { wasmURL, wasmModule }
}

await esbuild.initialize(makeInitOptions('/esbuild.wasm'))

Type guard

// Narrow an options object to a valid browser initialize payload.
type InitOpts = { wasmURL?: string; wasmModule?: WebAssembly.Module }
function hasWasmSource(o: InitOpts): o is { wasmURL: string } | { wasmModule: WebAssembly.Module } {
  return typeof o.wasmURL === 'string' || o.wasmModule instanceof WebAssembly.Module
}

Prevention

When it happens

Trigger: Calling esbuild.initialize() (or initialize({}) / initialize({ worker: true })) in the browser build without specifying where esbuild.wasm lives or supplying a precompiled WebAssembly.Module.

Common situations: Adapting a minimal example that omitted the wasmURL; serving the wasm from a CDN or asset path but forgetting to pass the option; intending to reuse a fetched module but passing undefined.

Related errors


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

Appendix: source

Thrown at lib/npm/browser.ts:76

  analyzeMetafile: typeof types.analyzeMetafile
}

let initializePromise: Promise<void> | undefined
let stopService: (() => void) | undefined
let longLivedService: Service | undefined

let ensureServiceIsRunning = (): Service => {
  if (longLivedService) return longLivedService
  if (initializePromise) throw new Error('You need to wait for the promise returned from "initialize" to be resolved before calling this')
  throw new Error('You need to call "initialize" before calling this')
}

export const initialize: typeof types.initialize = options => {
  options = common.validateInitializeOptions(options || {})
  let wasmURL = options.wasmURL
  let wasmModule = options.wasmModule
  let useWorker = options.worker !== false
  if (!wasmURL && !wasmModule) throw new Error('Must provide either the "wasmURL" option or the "wasmModule" option')
  if (initializePromise) throw new Error('Cannot call "initialize" more than once')
  initializePromise = startRunningService(wasmURL || '', wasmModule, useWorker)
  initializePromise.catch(() => {
    // Let the caller try again if this fails
    initializePromise = void 0
  })
  return initializePromise
}

const startRunningService = async (wasmURL: string | URL, wasmModule: WebAssembly.Module | undefined, useWorker: boolean): Promise<void> => {
  let worker: {
    onmessage: ((event: any) => void) | null
    postMessage: (data: Uint8Array | ArrayBuffer | WebAssembly.Module) => void
    terminate: () => void
  }

  let rejectAllWith: (error: unknown) => void
  const rejectAllPromise = new Promise(resolve => rejectAllWith = resolve)

View on GitHub (pinned to f6058f8364)