evanw/esbuild · error

Cannot compute relative path from

Error message

Cannot compute relative path from %q to %q

What it means

When both servedir and outdir are set in serve mode, esbuild computes a relative path from servedir to outdir using filepath.Rel(). This error fires when that computation fails, which can happen when the two paths are on different volumes (e.g. different drive letters on Windows).

Solutions

  1. Ensure servedir and outdir are on the same drive or volume
  2. Place outdir inside or adjacent to servedir
  3. Use an absolute outdir that shares a common prefix with servedir

Example fix

// before (Windows)
esbuild.serve({ servedir: 'C:\\serve' }) // outdir: D:\out
// after
esbuild.serve({ servedir: 'C:\\serve' }) // outdir: C:\\serve\out
Defensive patterns

Strategy: validation

Validate before calling

import path from 'path'
function validateServeOutdir(servedir, outdir) {
  const absServe = path.resolve(servedir)
  const absOut = path.resolve(outdir)
  // On Windows, check same drive letter
  if (process.platform === 'win32') {
    const serveDrive = absServe.substring(0, 2)
    const outDrive = absOut.substring(0, 2)
    if (serveDrive.toLowerCase() !== outDrive.toLowerCase()) {
      throw new Error(`servedir (${absServe}) and outdir (${absOut}) must be on the same drive`)
    }
  }
}
validateServeOutdir(serveOptions.servedir, buildOptions.outdir)

Prevention

When it happens

Trigger: Setting servedir and outdir to paths on different filesystem volumes or drives, making a relative path between them impossible to compute.

Common situations: On Windows, placing servedir on C:\ and outdir on D:\, or on Linux/macOS using paths that span different mount points in a way filepath.Rel cannot bridge.

Related errors


AI-assisted analysis of evanw/esbuild@f6058f8364 (2026-08-09). Data as JSON: /api/errors/248a88498d73b8bb. Report an issue: GitHub.

Appendix: source

Thrown at pkg/api/serve_other.go:808

	// Stuff related to the output directory only matters if there are entry points
	outdirPathPrefix := ""
	if len(ctx.args.entryPoints) > 0 {
		// Don't allow serving when builds are written to stdout
		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

View on GitHub (pinned to f6058f8364)