hashicorp/nomad · error
failed to resolve alloc directory: %w
Error message
failed to resolve alloc directory: %w
What it means
AllocDir.sanitizePath() resolves the alloc directory via filepath.EvalSymlinks before validating client-supplied paths. If the alloc dir cannot be resolved (it does not exist or a symlink component is broken/unreadable), this wrapped error is returned.
Source
Thrown at client/allocdir/alloc_dir.go:418
Name: info.Name(),
IsDir: info.IsDir(),
Size: info.Size(),
FileMode: info.Mode().String(),
ModTime: info.ModTime(),
}
}
return files, err
}
// sanitizePath checks that the path does not escape the alloc directory,
// does not read into the secrets or private directories and returns an absolute
// path of the provided path.
func (a *AllocDir) sanitizePath(path string) (string, error) {
// In some non linux systmes, directories like /var and /tmp resolve to
// /private/var and /private/tmp.
resolvedAllocDir, err := filepath.EvalSymlinks(a.AllocDir)
if err != nil {
return "", fmt.Errorf("failed to resolve alloc directory: %w", err)
}
requestedPath, err := filepath.Abs(filepath.Join(resolvedAllocDir, path))
if err != nil {
return "", fmt.Errorf("failed to resolve requested path: %w", err)
}
if err := escapingfs.ChildEscapesParentDir(resolvedAllocDir, requestedPath); err != nil {
return "", fmt.Errorf("path escapes the alloc directory")
}
a.mu.RLock()
defer a.mu.RUnlock()
// Check it does not access the secrets or private directories
for _, taskDir := range a.TaskDirs {
rps := strings.ReplaceAll(requestedPath, "/Secrets", "/secrets")
if err := escapingfs.ChildEscapesParentDir(taskDir.SecretsDir, rps); err == nil {View on GitHub (pinned to 482b49bf1a)
Solutions
- Verify the alloc dir still exists before issuing API calls; avoid racing with Destroy/GC
- Check that the alloc dir path and all symlink components are readable and resolvable
- Fix or remove dangling symlinks in the path
- Re-create the alloc dir via Build() if it was removed unintentionally
Example fix
// before
f, err := allocDir.ReadAt(path, offset)
// after
if _, err := os.Stat(allocDir.AllocDir); err != nil {
return nil, fmt.Errorf("alloc dir gone: %w", err)
}
f, err := allocDir.ReadAt(path, offset) Defensive patterns
Strategy: validation
Validate before calling
if _, err := filepath.EvalSymlinks(allocDir.AllocDir); err != nil {
return fmt.Errorf("alloc dir unavailable: %w", err)
} Try / catch
out, err := allocDir.List(relPath)
if err != nil && strings.Contains(err.Error(), "failed to resolve alloc directory") {
// alloc dir destroyed/GC'd: stop polling instead of retrying forever
return ErrAllocDirGone
} Prevention
- Stop file API polling when the allocation is terminal or GC'd
- Avoid dangling symlinks in the alloc dir path components
- Confirm alloc dir exists before issuing read APIs
- Handle the alloc-dir-destroyed case distinctly from retryable errors
When it happens
Trigger: Calling List, Stat, ReadAt, BlockUntilExists or ChangeEvents when filepath.EvalSymlinks(a.AllocDir) fails — typically because the alloc dir no longer exists (destroyed) or contains an unresolvable symlink.
Common situations: Filesystem API calls racing with allocation GC/Destroy which removed the alloc dir; host-dir paths that are dangling symlinks; permission issues preventing traversal of the alloc dir path.
Related errors
- error reading symlink: %v
- failed to resolve requested path: %w
- Couldn't resolve symlink for %v: %w
- Couldn't create symlink: %w
- error creating symlink: %w
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/64a59cd56f312747.
Report an issue: GitHub.