evanw/esbuild · error
Watch mode has already been enabled
Error message
Watch mode has already been enabled
What it means
Returned by internalContext.Watch when ctx.watcher is already non-nil, meaning watch mode is already running on this context. esbuild allows watch mode to be enabled at most once per context; calling Watch() a second time is a programming error. The check prevents duplicate file watchers and conflicting rebuild triggers.
Source
Thrown at pkg/api/api_impl.go:1084
return ctx.Rebuild()
}
func (ctx *internalContext) Rebuild() BuildResult {
return ctx.rebuild().result
}
func (ctx *internalContext) Watch(options WatchOptions) error {
ctx.mutex.Lock()
defer ctx.mutex.Unlock()
// Ignore disposed contexts
if ctx.didDispose {
return errors.New("Cannot watch a disposed context")
}
// Don't allow starting watch mode multiple times
if ctx.watcher != nil {
return errors.New("Watch mode has already been enabled")
}
logLevel := ctx.args.logOptions.LogLevel
ctx.watcher = &watcher{
fs: ctx.realFS,
shouldLog: logLevel == logger.LevelInfo || logLevel == logger.LevelDebug || logLevel == logger.LevelVerbose,
useColor: ctx.args.logOptions.Color,
pathStyle: ctx.args.logOptions.PathStyle,
rebuild: func() fs.WatchData {
return ctx.rebuild().watchData
},
delayInMS: time.Duration(options.Delay),
}
// All subsequent builds will be watch mode builds
ctx.args.options.WatchMode = true
// Start the file watcher goroutineView on GitHub (pinned to 6ff1d8b0d8)
Solutions
- Call Watch() exactly once per context; track a boolean so you don't re-invoke it.
- If you need to change watch behavior, dispose the context and create a new one before calling Watch() again.
- Move the Watch() call into context initialization so it cannot be reached twice.
- Guard the call: if (!watching) { ctx.Watch({}); watching = true; }
Example fix
// before
ctx.Watch({});
ctx.Watch({}); // -> Watch mode has already been enabled
// after
if (!watching) { ctx.Watch({}); watching = true; } Defensive patterns
Strategy: validation
Validate before calling
let watching = false;
function startWatch(ctx, opts) { if (watching) return; ctx.watch(opts); watching = true; } Type guard
function isAlreadyWatchingError(e) { return e?.message === 'Watch mode has already been enabled'; } Try / catch
try { ctx.Watch(opts); } catch (e) { if (isAlreadyWatchingError(e)) return; throw e; } Prevention
- Call Watch() exactly once per context.
- Use a boolean guard to prevent duplicate calls.
- To change watch config, recreate the context.
- Put Watch() in context initialization code only.
When it happens
Trigger: Calling ctx.Watch(opts) twice on the same esbuild context without disposing in between. The second call sees ctx.watcher != nil and returns the error.
Common situations: A dev tool that calls Watch() in a reload handler without checking if it already started; conditional code that invokes Watch() in two separate branches; wrapping esbuild and restarting watch on config edit without recreating the context.
Related errors
- Cannot watch a disposed context
- Serve mode has already been enabled
- The service was stopped
- Cannot serve a disposed context
- You need to wait for the promise returned from "initialize"
AI-assisted analysis of evanw/esbuild@6ff1d8b0d8 (2026-08-03).
Data as JSON: /data/errors/e93055fc54838f28.json.
Report an issue: GitHub.