evanw/esbuild · error · Error
Cannot use the "watch" API in this environment
Error message
Cannot use the "watch" API in this environment
What it means
The watch() API tells esbuild's native binary to keep running and rebuild on file changes; it requires the Go process to install filesystem watchers, which only works when hasFS is true. The guard at lib/shared/common.ts:1064 rejects watch() in the browser/wasm builds (hasFS:false). The native binary's watcher cannot observe the browser's virtual filesystem.
Source
Thrown at lib/shared/common.ts:1064
// In that situation we didn't get an "on-end" message since
// Go thought it wasn't necessary. In that situation, we
// trigger another rebuild below so that Go will (almost
// surely) send us an "on-end" message next time. I suspect
// that this is a very rare case, so the performance impact
// of building twice shouldn't really matter. It also only
// happens when "rebuild()" is used with "watch()" and/or
// "serve()".
triggerAnotherBuild()
}
})
}
triggerAnotherBuild()
})
return latestResultPromise
},
watch: (options = {}) => new Promise((resolve, reject) => {
if (!streamIn.hasFS) throw new Error(`Cannot use the "watch" API in this environment`)
const keys: OptionKeys = {}
const delay = getFlag(options, keys, 'delay', mustBeInteger)
checkForInvalidFlags(options, keys, `in watch() call`)
const request: protocol.WatchRequest = {
command: 'watch',
key: buildKey,
}
if (delay) request.delay = delay
sendRequest<protocol.WatchRequest, null>(refs, request, error => {
if (error) reject(new Error(error))
else resolve(undefined)
})
}),
serve: (options = {}) => new Promise((resolve, reject) => {
if (!streamIn.hasFS) throw new Error(`Cannot use the "serve" API in this environment`)
const keys: OptionKeys = {}
const port = getFlag(options, keys, 'port', mustBeValidPortNumber)View on GitHub (pinned to 6ff1d8b0d8)
Solutions
- Run watch() only under the Node or Deno (native) entry where hasFS is true.
- In the browser, implement your own change detection and call rebuild() manually instead of watch().
- Switch the import from 'esbuild-wasm' to 'esbuild' when running on a Node host.
Example fix
// before (browser wasm)
const ctx = await esbuild.context({ entryPoints: ['app.ts'] });
await ctx.watch();
// after — poll/rebuild yourself, or run under node
const ctx = await esbuild.context({ entryPoints: ['app.ts'] });
// no ctx.watch(); trigger rebuilds manually:
await ctx.rebuild(); Defensive patterns
Strategy: validation
Validate before calling
const hasFS = typeof process !== 'undefined' && !!process.versions?.node;
if (hasFS) {
await ctx.watch();
} else {
// implement manual rebuild loop in the browser
} Type guard
function canWatch(): boolean {
return typeof process !== 'undefined' && typeof process.versions?.node === 'string';
} Try / catch
try {
await ctx.watch();
} catch (e) {
if (/Cannot use the "watch" API/.test(e.message)) {
startManualWatcher();
} else throw e;
} Prevention
- Gate watch() behind an environment check (process.versions.node) before calling.
- Don't ship the browser/wasm entry where a Node dev-server script is expected.
- Use rebuild() + your own change detection in sandboxed runtimes.
When it happens
Trigger: Calling ctx.watch() on a context created from esbuild-wasm / @esbuild/wasm in the browser. Calling watch() in Deno wasm mode (lib/deno/wasm.ts:132 hasFS:false). Calling watch() inside a worker on the browser entrypoint.
Common situations: Online playground / in-browser bundler tries to replicate a dev server experience. Devs assume the WASM build is a drop-in replacement for the CLI. SSR edge runtimes that load esbuild but have no fs.
Related errors
- The "write" option is unavailable in this environment
- Cannot use the "serve" API in this environment
- ${quote(property)} must be an array of strings
- Must provide either the "wasmURL" option or the "wasmModule"
- The "wasmURL" option only works in the browser
AI-assisted analysis of evanw/esbuild@6ff1d8b0d8 (2026-08-03).
Data as JSON: /data/errors/40281503b4ccb44c.json.
Report an issue: GitHub.