evanw/esbuild · error
Serve mode has already been enabled
Error message
Serve mode has already been enabled
What it means
esbuild contexts support only one serve handler. This error fires when Serve() is called on a context that already has a non-nil handler field, indicating serve mode is already active.
Solutions
- Call serve() only once per context lifecycle
- Dispose the context and create a new one to restart serving with different options
- Guard with your own 'already serving' flag if multiple code paths can trigger serve
Example fix
// before
await ctx.serve({ port: 8000 })
await ctx.serve({ port: 9000 })
// after
await ctx.dispose()
const ctx2 = await esbuild.context(buildOptions)
await ctx2.serve({ port: 9000 }) Defensive patterns
Strategy: validation
Validate before calling
class ServeGuard {
constructor(ctx) { this.ctx = ctx; this.serving = false }
async serve(opts) {
if (this.serving) throw new Error('Serve already started on this context')
this.serving = true
return this.ctx.serve(opts)
}
} Prevention
- Call serve() only once per context from a single initialization point
- Create a new context to restart serving with different options
- Use a flag or wrapper class to prevent duplicate serve calls
When it happens
Trigger: Calling ctx.serve(...) twice on the same context without creating a new one.
Common situations: Dev tooling that restarts serve on config change without recreating the context, or calling serve in multiple initialization paths.
Related errors
- Cannot serve a disposed context
- Cannot watch a disposed context
- Watch mode has already been enabled
- Cannot compute relative path from
- Cannot serve without an output path
AI-assisted analysis of evanw/esbuild@f6058f8364 (2026-08-09).
Data as JSON: /api/errors/2d17fdc7b9941ec0.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/api/serve_other.go:758
func prettyPrintPath(fs fs.FS, path string) string {
if relPath, ok := fs.Rel(fs.Cwd(), path); ok {
return strings.ReplaceAll(relPath, "\\", "/")
}
return path
}
func (ctx *internalContext) Serve(serveOptions ServeOptions) (ServeResult, error) {
ctx.mutex.Lock()
defer ctx.mutex.Unlock()
// Ignore disposed contexts
if ctx.didDispose {
return ServeResult{}, errors.New("Cannot serve a disposed context")
}
// Don't allow starting serve mode multiple times
if ctx.handler != nil {
return ServeResult{}, errors.New("Serve mode has already been enabled")
}
// Don't allow starting serve mode multiple times
if (serveOptions.Keyfile != "") != (serveOptions.Certfile != "") {
return ServeResult{}, errors.New("Must specify both key and certificate for HTTPS")
}
// Validate the "servedir" path
if serveOptions.Servedir != "" {
if absPath, ok := ctx.realFS.Abs(serveOptions.Servedir); ok {
serveOptions.Servedir = absPath
} else {
return ServeResult{}, fmt.Errorf("Invalid serve path: %s", serveOptions.Servedir)
}
}
// Validate the "fallback" path
if serveOptions.Fallback != "" {View on GitHub (pinned to f6058f8364)