{"record":{"id":"87477277081c6e4f","repo":"hashicorp/nomad","slug":"could-not-stat-directory-v","errorCode":null,"errorMessage":"could not stat directory: %v","messagePattern":"could not stat directory: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"helper/escapingfs/copydir.go","lineNumber":29,"sourceCode":"\t\"path/filepath\"\n)\n\n// CopyDir copies a directory's contents to a new location, returning an error\n// on symlinks. This implementation is roughly the same as the stdlib os.CopyDir\n// but with th e important difference that we preserve file modes.\nfunc CopyDir(src, dst string) error {\n\tsrcFs := os.DirFS(src)\n\n\treturn fs.WalkDir(srcFs, \".\", func(oldPath string, d fs.DirEntry, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tnewPath := filepath.Join(dst, oldPath)\n\t\tif d.IsDir() {\n\t\t\tinfo, err := d.Info()\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"could not stat directory: %v\", err)\n\t\t\t}\n\t\t\treturn os.MkdirAll(newPath, info.Mode())\n\t\t}\n\t\tif !d.Type().IsRegular() {\n\t\t\treturn fmt.Errorf(\"copying cannot traverse symlinks\")\n\t\t}\n\n\t\tr, err := srcFs.Open(oldPath)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could not open existing file: %v\", err)\n\t\t}\n\t\tdefer r.Close()\n\t\tinfo, err := r.Stat()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could not stat file: %v\", err)\n\t\t}\n\n\t\tw, err := os.OpenFile(newPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, info.Mode())","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/helper/escapingfs/copydir.go#L11-L47","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nerr := escapingfs.CopyDir(\"/live/data\", dst) // dir deleted mid-walk\n// after\nsnapshot, _ := os.MkdirTemp(\"\", \"snapshot\")\nexec.Command(\"cp\", \"-a\", \"/live/data/.\", snapshot).Run()\nerr := escapingfs.CopyDir(snapshot, dst)","handlingStrategy":"try-catch","validationCode":"// Preflight: ensure source tree is readable before CopyDir\nif err := filepath.WalkDir(src, func(p string, d fs.DirEntry, err error) error {\n\tif err != nil { return err }\n\tif d.IsDir() { _, err := d.Info(); return err }\n\treturn nil\n}); err != nil {\n\treturn fmt.Errorf(\"source tree not stable/readable: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"err := escapingfs.CopyDir(src, dst)\nif err != nil {\n\tif strings.Contains(err.Error(), \"could not stat directory\") {\n\t\t// transient race or permission issue: retry once after a short wait\n\t\ttime.Sleep(100 * time.Millisecond)\n\t\terr = escapingfs.CopyDir(src, dst)\n\t}\n\treturn err\n}","preventionTips":["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"],"tags":["filesystem","copy","stat","race-condition"],"backgroundTag":"stat-failed","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}