hashicorp/nomad · error
could not stat directory: %v
Error message
could not stat directory: %v
What it means
CopyDir in helper/escapingfs walks the source directory tree via fs.WalkDir; when it encounters a directory entry it calls d.Info() to read the directory's metadata so the mode can be preserved. This error wraps a failure of that Info() call, meaning the directory's stat information could not be retrieved during the copy. The copy aborts at that point.
Source
Thrown at helper/escapingfs/copydir.go:29
"path/filepath"
)
// CopyDir copies a directory's contents to a new location, returning an error
// on symlinks. This implementation is roughly the same as the stdlib os.CopyDir
// but with th e important difference that we preserve file modes.
func CopyDir(src, dst string) error {
srcFs := os.DirFS(src)
return fs.WalkDir(srcFs, ".", func(oldPath string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
newPath := filepath.Join(dst, oldPath)
if d.IsDir() {
info, err := d.Info()
if err != nil {
return fmt.Errorf("could not stat directory: %v", err)
}
return os.MkdirAll(newPath, info.Mode())
}
if !d.Type().IsRegular() {
return fmt.Errorf("copying cannot traverse symlinks")
}
r, err := srcFs.Open(oldPath)
if err != nil {
return fmt.Errorf("could not open existing file: %v", err)
}
defer r.Close()
info, err := r.Stat()
if err != nil {
return fmt.Errorf("could not stat file: %v", err)
}
w, err := os.OpenFile(newPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, info.Mode())View on GitHub (pinned to 482b49bf1a)
Solutions
- Re-run the copy once the source tree is stable; ensure no concurrent process deletes/renames directories under src.
- Check and fix read permissions on the affected source directory (the wrapped %v names it via the walk path).
- Copy from local disk instead of a network/ephemeral mount if stat calls are unreliable there.
- If racing is unavoidable, snapshot the source (e.g. rsync/cp to a staging dir) and CopyDir from the snapshot.
Example fix
// before
err := escapingfs.CopyDir("/live/data", dst) // dir deleted mid-walk
// after
snapshot, _ := os.MkdirTemp("", "snapshot")
exec.Command("cp", "-a", "/live/data/.", snapshot).Run()
err := escapingfs.CopyDir(snapshot, dst) Defensive patterns
Strategy: try-catch
Validate before calling
// Preflight: ensure source tree is readable before CopyDir
if err := filepath.WalkDir(src, func(p string, d fs.DirEntry, err error) error {
if err != nil { return err }
if d.IsDir() { _, err := d.Info(); return err }
return nil
}); err != nil {
return fmt.Errorf("source tree not stable/readable: %w", err)
} Try / catch
err := escapingfs.CopyDir(src, dst)
if err != nil {
if strings.Contains(err.Error(), "could not stat directory") {
// transient race or permission issue: retry once after a short wait
time.Sleep(100 * time.Millisecond)
err = escapingfs.CopyDir(src, dst)
}
return err
} Prevention
- Do not mutate the source tree while CopyDir is running
- Copy from a snapshot or staging copy of live data
- Ensure the walking process has read permission on all source subdirectories
- Avoid copying directly from network/ephemeral mounts prone to stat failures
When it happens
Trigger: Calling CopyDir(src, dst) when fs.WalkDir yields a directory whose DirEntry.Info() fails — e.g. the directory was deleted or renamed between the walk listing and the Info() call (TOCTOU race), or permission to stat it was revoked.
Common situations: Copying a live directory tree that another process is concurrently pruning; copying from a mount that is being unmounted or a flaky network filesystem; permission changes (chmod/ACL) applied while walking the tree.
Related errors
- error reading symlink: %v
- Couldn't copy %q to %q: %w
- Couldn't read the file information %v: %w
- copying cannot traverse symlinks
- could not stat file: %v
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/87477277081c6e4f.
Report an issue: GitHub.