evanw/esbuild · error
Cannot serve a disposed context
Error message
Cannot serve a disposed context
What it means
This error fires when Serve() is called on an esbuild context that has already been disposed. The context checks the didDispose flag and refuses to start a serve handler because all internal state has been torn down.
Solutions
- Create a fresh context with esbuild.context(options) before calling serve
- Coordinate serve and dispose ordering in your server shutdown handler
- Null out the context reference after dispose to catch stale usage early
Example fix
// before
await ctx.dispose()
await ctx.serve({ servedir: '.' })
// after
await ctx.dispose()
const ctx2 = await esbuild.context(buildOptions)
await ctx2.serve({ servedir: '.' }) Defensive patterns
Strategy: try-catch
Validate before calling
class ServeManager {
constructor(ctx) { this.ctx = ctx; this.disposed = false }
async serve(opts) {
if (this.disposed) throw new Error('Context already disposed — create a new context')
return this.ctx.serve(opts)
}
async dispose() {
if (this.disposed) return
this.disposed = true
await this.ctx.dispose()
}
} Try / catch
try {
await ctx.serve(serveOptions)
} catch (e) {
if (e.message.includes('disposed context')) {
ctx = await esbuild.context(buildOptions)
await ctx.serve(serveOptions)
} else {
throw e
}
} Prevention
- Coordinate serve and dispose calls in your server lifecycle manager
- Null out context references after dispose to catch stale usage
- Never assume a context is still valid after a dispose call
When it happens
Trigger: Calling await ctx.dispose() and then await ctx.serve(options) on the same context object.
Common situations: Dev server lifecycle bugs where a context is disposed during hot-reload or config change and then serve is called again on the stale reference.
Related errors
- Serve mode has already been enabled
- 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/2b78ce447b43b4b1.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/api/serve_other.go:753
return []byte(html.String())
}
// This is used to make error messages platform-independent
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)View on GitHub (pinned to f6058f8364)