hashicorp/nomad · error
alloc dir must be absolute
Error message
alloc dir must be absolute
What it means
PathEscapesAllocDir checks whether a path escapes a Nomad allocation directory. It requires the `base` (alloc dir) argument to be an absolute path; if a caller passes a relative base, filepath.Join would produce an unusable relative root and escape detection would be unreliable, so the function fails fast with this error.
Source
Thrown at helper/escapingfs/escapes.go:119
func hasPrefixCaseInsensitive(path, prefix string) bool {
if len(prefix) > len(path) {
return false
}
return strings.EqualFold(path[:len(prefix)], prefix)
}
// PathEscapesAllocDir returns true if base/prefix/path escapes the given base directory.
//
// Escaping a directory can be done with relative paths (e.g. ../../ etc.) or by
// using symlinks. This checks both methods.
//
// The base directory must be an absolute path.
func PathEscapesAllocDir(base, prefix, path string) (bool, error) {
full := filepath.Join(base, prefix, path)
// If base is not an absolute path, the caller passed in the wrong thing.
if !filepath.IsAbs(base) {
return false, errors.New("alloc dir must be absolute")
}
// Check path does not escape the alloc dir using relative paths.
if escapes, err := PathEscapesAllocViaRelative(prefix, path); err != nil {
return false, err
} else if escapes {
return true, nil
}
// Check path does not escape the alloc dir using symlinks.
if escapes, err := pathEscapesBaseViaSymlink(base, full); err != nil {
if os.IsNotExist(err) {
// Treat non-existent files as non-errors; perhaps not ideal but we
// have existing features (log-follow) that depend on this. Still safe,
// because we do the symlink check on every ReadAt call also.
return false, nil
}
return false, errView on GitHub (pinned to 482b49bf1a)
Solutions
- Set client.alloc_dir in the Nomad agent config to an absolute path (e.g. /opt/nomad/data/alloc) and restart the agent.
- If calling PathEscapesAllocDir directly, wrap the base with filepath.Abs() before calling.
- Verify downstream callers (e.g. alloc dir HTTP streaming handlers) propagate an absolute dir, not a user-supplied relative one.
Example fix
// before
escapes, err := PathEscapesAllocDir("alloc", prefix, path)
// after
absBase, err := filepath.Abs("alloc")
if err != nil { return err }
escapes, err := PathEscapesAllocDir(absBase, prefix, path) Defensive patterns
Strategy: validation
Validate before calling
if !filepath.IsAbs(base) {
return fmt.Errorf("alloc dir %q must be absolute", base)
}
escapes, err := PathEscapesAllocDir(base, prefix, path) Type guard
func isAbsBase(base string) bool { return filepath.IsAbs(base) } Try / catch
escapes, err := PathEscapesAllocDir(base, prefix, path)
if err != nil {
if err.Error() == "alloc dir must be absolute" {
abs, aerr := filepath.Abs(base)
if aerr != nil { return aerr }
escapes, err = PathEscapesAllocDir(abs, prefix, path)
}
if err != nil { return err }
} Prevention
- Always derive the alloc dir from an absolute client data_dir in agent config.
- Call filepath.Abs at configuration-load time, not at request time.
- Add a startup config check rejecting relative alloc_dir values.
When it happens
Trigger: Calling helper/escapingfs.PathEscapesAllocDir(base, prefix, path) with a non-absolute `base` value, e.g. "alloc/" or "./alloc". It is reached indirectly via callers like streamAllocDir (HTTP alloc dir streaming) when the configured client alloc_dir is relative.
Common situations: Nomad agent configs with `client { alloc_dir = "nomad-data" }` (relative path); manually invoking the helper in tests/tools with a relative base; code constructing the alloc dir from a relative working directory.
Related errors
- plugin not found
- could not verify host path for %q: %w
- intro token file is a directory
- work_dir must be an absolute path: %s
- unable to read rooted allocation directory
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/ae1bd804e9584955.
Report an issue: GitHub.