hashicorp/nomad · error
copying cannot traverse symlinks
Error message
copying cannot traverse symlinks
What it means
CopyDir walks a source directory tree and copies it to a new location, but it deliberately refuses to follow anything that is not a regular file (symlinks, devices, sockets, etc.). When the fs.WalkDir callback encounters such an entry, it returns this error to abort the copy, preventing symlink-based path escape or copying of non-file entries from the escapingfs package.
Source
Thrown at helper/escapingfs/copydir.go:34
// 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())
if err != nil {
return err
}
if _, err := io.Copy(w, r); err != nil {View on GitHub (pinned to 482b49bf1a)
Solutions
- Find the offending symlink or non-regular entry in the source directory (e.g. `find <srcdir> ! -type f -not -type d`) and remove it or replace it with a real file
- Copy the dereferenced contents instead: use `cp -rL` semantics or resolve the symlink target and copy it as a regular file
- If symlinks are intentional, do not use escapingfs.CopyDir; use a copy helper that follows or copies symlinks explicitly (e.g. filepath.WalkDir with custom symlink handling or symlinks/copy via os.Readlink + os.Symlink)
- If the symlink is generated by another component, fix that component to materialize regular files instead of links
Example fix
// before $ find ./task-dir -type l ./task-dir/lib -> /usr/lib/libfoo.so // after $ rm ./task-dir/lib $ cp /usr/lib/libfoo.so ./task-dir/lib
Defensive patterns
Strategy: validation
Validate before calling
func assertNoSymlinks(src string) error {
return filepath.WalkDir(src, func(p string, d fs.DirEntry, err error) error {
if err != nil { return err }
if !d.IsDir() && !d.Type().IsRegular() {
return fmt.Errorf("non-regular entry %s (type %s) in %s", p, d.Type(), src)
}
return nil
})
}
// call assertNoSymlinks(src) before CopyDir(src, dst) Prevention
- Scan source directories with `find <dir> ! -type f ! -type d` before copying
- Disable or review tooling that creates symlinks inside managed directories
- Handle the error by checking os.IsNotExist/Lstat on the reported path to identify the offending entry
When it happens
Trigger: Calling escapingfs.CopyDir on a source directory that contains a symlink (or any non-regular entry: device, socket, named pipe) anywhere in its tree.
Common situations: Copying job/task directories that contain symlinks created by the OS or user (e.g. Linux client data dirs with symlinks to shared libraries or chroot links); copy operations on directories managed by tools that link shared assets; environments where a symlink was accidentally left in a data directory.
Related errors
- error reading symlink: %v
- failed to resolve alloc directory: %w
- Couldn't copy %q to %q: %w
- Couldn't resolve symlink for %v: %w
- Couldn't create symlink: %w
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/878535e494d7e10d.
Report an issue: GitHub.