evanw/esbuild · error
Output directory %q must be contained in serve directory %q
Error message
Output directory %q must be contained in serve directory %q
What it means
Returned by internalContext.Serve when the computed relative path from servedir to AbsOutputDir starts with '..' (i.e. the output directory is outside the serve directory). For the HTTP server to serve build output, the output dir must be inside (or equal to) the serve dir; otherwise the files would be unreachable via URLs. The check tests relPath == '..' or strings.HasPrefix(relPath, '../').
Source
Thrown at pkg/api/serve_other.go:796
if ctx.args.options.WriteToStdout {
what := "entry points"
if len(ctx.args.entryPoints) == 1 {
what = "an entry point"
}
return ServeResult{}, fmt.Errorf("Cannot serve %s without an output path", what)
}
// Compute the output path prefix
if serveOptions.Servedir != "" && ctx.args.options.AbsOutputDir != "" {
// Make sure the output directory is contained in the "servedir" directory
relPath, ok := ctx.realFS.Rel(serveOptions.Servedir, ctx.args.options.AbsOutputDir)
if !ok {
return ServeResult{}, fmt.Errorf(
"Cannot compute relative path from %q to %q\n", serveOptions.Servedir, ctx.args.options.AbsOutputDir)
}
relPath = strings.ReplaceAll(relPath, "\\", "/") // Fix paths on Windows
if relPath == ".." || strings.HasPrefix(relPath, "../") {
return ServeResult{}, fmt.Errorf(
"Output directory %q must be contained in serve directory %q",
prettyPrintPath(ctx.realFS, ctx.args.options.AbsOutputDir),
prettyPrintPath(ctx.realFS, serveOptions.Servedir),
)
}
if relPath != "." {
outdirPathPrefix = relPath
}
}
}
// Determine the host
var listener net.Listener
network := "tcp4"
host := "0.0.0.0"
hostIsIP := true
if serveOptions.Host != "" {
host = serveOptions.HostView on GitHub (pinned to 6ff1d8b0d8)
Solutions
- Move outdir to be inside servedir (e.g. servedir='./www', outdir='./www/assets').
- Set servedir to a parent that contains outdir.
- If outdir must be elsewhere, drop servedir and serve the outdir directly instead.
- Verify with a quick path check that outdir is a subdirectory of servedir before Serve().
Example fix
// before
ctx = await api.Context({ entryPoints: ['src/index.js'], outdir: './dist' });
ctx.Serve({ Servedir: './public' }); // dist is outside public -> error
// after
ctx = await api.Context({ entryPoints: ['src/index.js'], outdir: './public/dist' });
ctx.Serve({ Servedir: './public' }); Defensive patterns
Strategy: validation
Validate before calling
const path = require('path');
function isContained(child, parent) {
const rc = path.resolve(child), rp = path.resolve(parent);
return rc === rp || rc.startsWith(rp + path.sep);
}
if (!isContained(outdir, servedir)) throw new Error('outdir must be inside servedir'); Try / catch
try { await ctx.Serve({ Servedir }); } catch (e) { if (/must be contained in serve directory/.test(e.message)) { /* move outdir under servedir */ } throw e; } Prevention
- Put outdir inside servedir.
- Or set servedir to a common parent of outdir.
- Validate containment with path checks before Serve().
- Avoid '../' in outdir relative to servedir.
When it happens
Trigger: Serve() with a Servedir and an AbsOutputDir where outdir is a sibling of or above servedir. Rel succeeds but yields a path beginning with '..', so the guard at serve_other.go:795 fires.
Common situations: Setting outdir to '../dist' while servedir is './public'; outdir and servedir as siblings; a default outdir that happens to live outside the served static dir; misconfigured monorepo paths.
Related errors
- Invalid serve path: %s
- Invalid fallback path: %s
- Invalid origin: %s
- Must specify both key and certificate for HTTPS
- Cannot serve %s without an output path
AI-assisted analysis of evanw/esbuild@6ff1d8b0d8 (2026-08-03).
Data as JSON: /data/errors/b7b593a50f58efad.json.
Report an issue: GitHub.