evanw/esbuild · error
Rel: can't make {targpath} relative to {basepath}
Error message
Rel: can't make {targpath} relative to {basepath} What it means
esbuild vendors filepath.Rel in internal/fs/filepath.go. This branch returns the error when Rel cannot relate targpath to basepath because the two paths are on different volumes (e.g. different Windows drive letters C: vs D:) or one is volume-rooted/absolute-slashed and the other is not. Rel fundamentally requires both paths to share a volume and rootedness.
Source
Thrown at internal/fs/filepath.go:593
// Rel calls Clean on the result.
func (fp goFilepath) rel(basepath, targpath string) (string, error) {
baseVol := fp.volumeName(basepath)
targVol := fp.volumeName(targpath)
base := fp.clean(basepath)
targ := fp.clean(targpath)
if fp.sameWord(targ, base) {
return ".", nil
}
base = base[len(baseVol):]
targ = targ[len(targVol):]
if base == "." {
base = ""
}
// Can't use IsAbs - `\a` and `a` are both relative in Windows.
baseSlashed := len(base) > 0 && base[0] == fp.pathSeparator
targSlashed := len(targ) > 0 && targ[0] == fp.pathSeparator
if baseSlashed != targSlashed || !fp.sameWord(baseVol, targVol) {
return "", errors.New("Rel: can't make " + targpath + " relative to " + basepath)
}
// Position base[b0:bi] and targ[t0:ti] at the first differing elements.
bl := len(base)
tl := len(targ)
var b0, bi, t0, ti int
for {
for bi < bl && base[bi] != fp.pathSeparator {
bi++
}
for ti < tl && targ[ti] != fp.pathSeparator {
ti++
}
if !fp.sameWord(targ[t0:ti], base[b0:bi]) {
break
}
if bi < bl {
bi++
}View on GitHub (pinned to 6ff1d8b0d8)
Solutions
- Ensure both paths are on the same volume/drive (e.g. move outdir under the same drive as servedir).
- Pass absolute paths consistently to both arguments so rootedness matches.
- On Windows, normalize drive letters / UNC prefixes so they match between the two paths.
- If using esbuild serve, keep AbsOutputDir and Servedir on the same filesystem root.
Example fix
// before (Windows, cross-volume)
ctx.Serve({ Servedir: "C:\\www", /* outdir = D:\\build" })
// after (same volume)
ctx.Serve({ Servedir: "C:\\www", /* outdir = C:\\www\\build" }) Defensive patterns
Strategy: validation
Validate before calling
const path = require('path');
function sameVolume(a, b) {
const ra = path.resolve(a), rb = path.resolve(b);
// Windows: compare drive letter / UNC root
return ra.split(path.sep)[0].toLowerCase() === rb.split(path.sep)[0].toLowerCase();
}
if (!sameVolume(servedir, outdir)) throw new Error('cross-volume paths'); Try / catch
try { await ctx.Serve({ Servedir, /* outdir set */ }); } catch (e) { if (/can't make .* relative to/.test(e.message)) { /* move outdir onto same volume as servedir */ } throw e; } Prevention
- Keep outdir and servedir on the same drive/volume.
- Pass absolute paths consistently.
- On Windows, normalize drive letters and UNC prefixes.
- Validate volume equality before calling Serve().
When it happens
Trigger: rel(basepath, targpath) is called where baseVol != targVol (different drive letters / UNC hosts) or where one path begins with a path separator and the other does not (baseSlashed != targSlashed). In esbuild this surfaces from computeRelativePath when the output directory and serve directory live on different volumes, or from watch-file relativization across drives.
Common situations: On Windows, outdir on D:\ while servedir is on C:\ (or vice versa); mixing a UNC path (\\server\share) with a drive path; one path passed as relative (no leading slash) while the other is absolute; cross-volume builds where the project and build output are on different mounts.
Related errors
- EvalSymlinks: too many links
- Cannot compute relative path from %q to %q
- Invalid serve path: %s
- Invalid fallback path: %s
- The "write" option is unavailable in this environment
AI-assisted analysis of evanw/esbuild@6ff1d8b0d8 (2026-08-03).
Data as JSON: /data/errors/4d665f392fe1ee76.json.
Report an issue: GitHub.