hashicorp/nomad · error
artifact destination path escapes alloc directory
Error message
artifact destination path escapes alloc directory
What it means
getDestination resolves the artifact's RelativeDest via env.ClientPath; if the resolved path escapes the allocation directory (escapes == true), Nomad refuses it with this non-recoverable Error. This is a sandbox-escape guard: artifacts must land inside the alloc dir, and Nomad rejects destinations that would write outside it.
Source
Thrown at client/allocrunner/taskrunner/getter/util.go:75
q.Set(k, taskEnv.ReplaceEnv(v))
}
u.RawQuery = q.Encode()
// add the prefix back if necessary
sourceURL := u.String()
if gitSSH {
sourceURL = fmt.Sprintf("%s%s", githubPrefixSSH, sourceURL)
}
return sourceURL, nil
}
func getDestination(env interfaces.EnvReplacer, artifact *structs.TaskArtifact) (string, error) {
destination, escapes := env.ClientPath(artifact.RelativeDest, true)
if escapes {
return "", &Error{
URL: artifact.GetterSource,
Err: fmt.Errorf("artifact destination path escapes alloc directory"),
Recoverable: false,
}
}
return destination, nil
}
func getMode(artifact *structs.TaskArtifact) getter.ClientMode {
switch artifact.GetterMode {
case structs.GetterModeFile:
return getter.ClientModeFile
case structs.GetterModeDir:
return getter.ClientModeDir
default:
return getter.ClientModeAny
}
}
func chownDestination(destination, username string) error {View on GitHub (pinned to 482b49bf1a)
Solutions
- Set RelativeDest to a relative path inside the task directory (e.g. "local/data.txt" or "sub/dir/file")
- Remove any leading '/' or '..' segments from RelativeDest
- If you need data elsewhere, download to local/ and copy/symlink within the task via a template or script task
- Check interpolated env attributes used in RelativeDest to ensure the expansion stays within alloc dir
Example fix
// before
artifact {
source = "https://example.com/bin.tgz"
destination = "/usr/local/bin"
}
// after
artifact {
source = "https://example.com/bin.tgz"
destination = "local/bin"
} Defensive patterns
Strategy: validation
Validate before calling
// ensure destination stays inside the task dir
if filepath.IsAbs(dest) || strings.HasPrefix(filepath.Clean(dest), "..") {
return fmt.Errorf("destination %q must be relative to the task dir", dest)
} Try / catch
// non-recoverable: surface to user without retry
var gerr *getter.Error
if errors.As(err, &gerr) && !gerr.Recoverable {
failTask(gerr) // no retry
} Prevention
- Always use paths relative to the task directory for destination
- Never use leading '/' or '..' in RelativeDest
- Interpolate carefully — verify env expansions resolve within alloc dir
- Download to local/ and relocate via later task steps
When it happens
Trigger: TaskArtifact.RelativeDest resolves (via env interpolation and path cleaning) outside the task's alloc directory — e.g. values like "../other/file", absolute paths outside alloc dir, or interpolation to an env var containing such a path
Common situations: Using '../' segments or absolute paths like '/etc/foo' in RelativeDest, expecting pre-1.x behavior where looser destinations were tolerated, or interpolating with node/task attributes that produce a path pointing out of the sandbox
Related errors
- artifact includes symlink that resolves outside of sandbox
- path escapes the alloc directory
- Reading secret file prohibited: %s
- failed to parse source URL %q: %v
- getter subprocess failed: %v: %v
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/a353e26f97902322.
Report an issue: GitHub.