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, err

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Set client.alloc_dir in the Nomad agent config to an absolute path (e.g. /opt/nomad/data/alloc) and restart the agent.
  2. If calling PathEscapesAllocDir directly, wrap the base with filepath.Abs() before calling.
  3. 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

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


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/ae1bd804e9584955. Report an issue: GitHub.