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

  1. Call serve() only once per context lifecycle
  2. Dispose the context and create a new one to restart serving with different options
  3. 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

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


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)