evanw/esbuild · error · Error

The "worker" option only works in the browser

Error message

The "worker" option only works in the browser

What it means

esbuild's Node entry spawns the native binary directly and never uses a Web Worker, so the `worker` option to initialize() is meaningless there and is explicitly rejected. The `worker` flag exists only for the browser/WASM build (esbuild-wasm), where it spawns a Web Worker to run WebAssembly off the main thread. Passing it in Node throws at lib/npm/node.ts:236 inside initialize().

Solutions

  1. Remove the `worker` property from the options passed to initialize() when running in Node.
  2. If you genuinely need WASM in Node, install and import `esbuild-wasm` instead of `esbuild` (there `worker` is honored).
  3. Branch your config on environment: only set `worker` when `typeof window !== 'undefined'` / using the wasm package.

Example fix

// before
import { initialize } from 'esbuild'
await initialize({ worker: true })

// after
import { initialize } from 'esbuild'
await initialize({})
Defensive patterns

Strategy: validation

Validate before calling

// Before calling initialize, drop browser-only keys when running in Node
function sanitizeInitOptions(opts) {
  const cleaned = { ...opts }
  if (typeof process !== 'undefined' && process.versions?.node) {
    delete cleaned.worker   // native esbuild ignores/rejects this
    delete cleaned.wasmURL
    delete cleaned.wasmModule
  }
  return cleaned
}
await initialize(sanitizeInitOptions(myOpts))

Prevention

When it happens

Trigger: Calling `initialize({ worker: true })` (or any truthy value) while importing the native `esbuild` package in Node.js. The initialize() function at lib/npm/node.ts:232 validates options then rejects `worker` (alongside `wasmURL` and `wasmModule`) before starting the service.

Common situations: Copying an esbuild-wasm browser initialization snippet into a Node script; sharing one config object between a browser (esbuild-wasm) frontend and a Node (esbuild) build; migrating from esbuild-wasm to native esbuild and forgetting to drop the worker flag.

Related errors


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

Appendix: source

Thrown at lib/npm/node.ts:236

    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
let stopService: (() => Promise<void>) | undefined

View on GitHub (pinned to f6058f8364)