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
- Remove the `worker` property from the options passed to initialize() when running in Node.
- If you genuinely need WASM in Node, install and import `esbuild-wasm` instead of `esbuild` (there `worker` is honored).
- 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
- Only set `worker` when importing from esbuild-wasm in a browser context.
- Keep your Node build config and browser wasm config in separate modules.
- Run a smoke-test build in CI to catch init-option mistakes early.
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
- Cannot use the "serve" API in this environment
- Cannot use the "watch" API in this environment
- The "write" option is unavailable in this environment
- Cannot serve without an output path
- Expected in mangle cache to map to either a string or false
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)