hashicorp/nomad · error
error chowning directory %w
Error message
error chowning directory %w
What it means
While extracting a directory entry from the alloc dir tar archive, Nomad creates the directory then attempts os.Chown with the Uid/Gid from the tar header. This error is returned when the chown fails — only attempted when running as euid 0 (root) on non-Windows.
Source
Thrown at client/allocwatcher/alloc_watcher.go:622
// the message out of the file and return it.
errBuf := make([]byte, int(hdr.Size))
if _, err := tr.Read(errBuf); err != nil && err != io.EOF {
return fmt.Errorf("error streaming previous alloc %q for new alloc %q; failed reading error message: %w",
p.prevAllocID, p.allocID, err)
}
return fmt.Errorf("error streaming previous alloc %q for new alloc %q: %s",
p.prevAllocID, p.allocID, string(errBuf))
}
// If the header is for a directory we create the directory
if hdr.Typeflag == tar.TypeDir {
name := filepath.Join(dest, hdr.Name)
os.MkdirAll(name, os.FileMode(hdr.Mode))
// Can't change owner if not root or on Windows.
if euid == 0 {
if err := os.Chown(name, hdr.Uid, hdr.Gid); err != nil {
return fmt.Errorf("error chowning directory %w", err)
}
}
continue
}
// If the header is for a symlink we create the symlink
if hdr.Typeflag == tar.TypeSymlink {
if err = os.Symlink(hdr.Linkname, filepath.Join(dest, hdr.Name)); err != nil {
return fmt.Errorf("error creating symlink: %w", err)
}
for _, path := range []string{hdr.Name, hdr.Linkname} {
if escapes, err := escapingfs.PathEscapesAllocDir(dest, "", path); err != nil {
return fmt.Errorf("error evaluating symlink: %w", err)
} else if escapes {
return fmt.Errorf("archive contains symlink that escapes alloc dir")
}
}
View on GitHub (pinned to 482b49bf1a)
Solutions
- Ensure the filesystem backing the data dir supports chown (avoid root-squashed NFS)
- Grant the Nomad client CAP_CHOWN if running in a container as root
- Verify the tar header Uid/Gid values are valid on the destination host
- Run the client as a non-root user to skip chown, if ownership does not matter
Defensive patterns
Strategy: validation
Validate before calling
// Confirm the destination filesystem supports chown and the client has the capability: // touch a test file and os.Chown it with the expected uid/gid before starting the client
Try / catch
if err := watcher.Wait(ctx); err != nil {
var pathErr *os.PathError
if errors.As(err, &pathErr) && errors.Is(pathErr.Err, syscall.EPERM) {
// fall back: run client without chown or fix capabilities
}
return err
} Prevention
- Use local POSIX filesystems (ext4/xfs) for the client data dir
- Grant CAP_CHOWN when running the client in containers as root
- Keep consistent UID/GID ranges across the cluster
When it happens
Trigger: Running as root, streamAllocDir processes a tar.TypeDir header, MkdirAll succeeds, but os.Chown on the new directory fails (e.g. Uid/Gid not valid on the host, filesystem does not support chown).
Common situations: Migration across nodes with mismatched UID/GID ranges; containerized root lacking CAP_CHOWN; network filesystems (NFS with root squashing) rejecting chown.
Related errors
- Couldn't change owner/group of %v to (uid: %v, gid: %v): %w
- error chowning file %w
- plugin not executable
- Chmod(%v) failed: %w
- unable to remove existing unix socket: %w
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/8441668d92e16f23.
Report an issue: GitHub.