{"record":{"id":"81aa5d387fc73d15","repo":"hashicorp/nomad","slug":"couldn-t-resolve-symlink-for-v-w","errorCode":null,"errorMessage":"Couldn't resolve symlink for %v: %w","messagePattern":"Couldn't resolve symlink for (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/allocdir/task_dir.go","lineNumber":294,"sourceCode":"\t\t\t\tsubdirs[hostEntry] = filepath.Join(dest, filepath.Base(hostEntry))\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Check if entry exists. This can happen if restarting a failed\n\t\t\t// task.\n\t\t\tif _, err := os.Lstat(taskEntry); err == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif !entry.Mode().IsRegular() {\n\t\t\t\t// If it is a symlink we can create it, otherwise we skip it.\n\t\t\t\tif entry.Mode()&os.ModeSymlink == 0 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tlink, err := os.Readlink(hostEntry)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"Couldn't resolve symlink for %v: %w\", source, err)\n\t\t\t\t}\n\n\t\t\t\tif err := os.Symlink(link, taskEntry); err != nil {\n\t\t\t\t\t// Symlinking twice\n\t\t\t\t\tif err.(*os.LinkError).Err.Error() != \"file exists\" {\n\t\t\t\t\t\treturn fmt.Errorf(\"Couldn't create symlink: %w\", err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tuid, gid := getOwner(entry)\n\t\t\tif err := linkOrCopy(hostEntry, taskEntry, uid, gid, entry.Mode().Perm()); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n","sourceCodeStart":276,"sourceCodeEnd":312,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/allocdir/task_dir.go#L276-L312","documentation":"embedDirs walks a host directory tree and mirrors symlinks into the task's chroot directory. This error wraps os.Readlink failing to resolve a symlink on the host, meaning the link target could not be read (e.g. the link vanished mid-walk, or a permission/IO problem). It aborts chroot building for the allocation.","triggerScenarios":"Building the chroot (buildChroot or recursive embedDirs) while reading host directories; os.Readlink(hostEntry) returns an error such as ENOENT (symlink removed concurrently), EACCES (no permission on parent dir), or ELOOP (symlink chain too deep).","commonSituations":"Host paths included in the chroot map (e.g. /etc, /usr) contain symlinks that are mutated during traversal by package managers or administrators; running the Nomad client with a user lacking read permission on the linked parent directory; deep symlink recursion on unusual filesystems.","solutions":["Check the wrapped underlying error (%w) to identify the failing path and errno; verify the host path exists and is a readable symlink with `ls -l` and `readlink` as the Nomad client user","Re-run the allocation — transient races (link removed during walk) usually succeed on retry","Ensure the Nomad agent runs with sufficient filesystem permissions to traverse all chroot-mapped host directories","Review the client's chroot map configuration and exclude volatile host directories"],"exampleFix":"// before\nlink, err := os.Readlink(hostEntry)\nif err != nil {\n    return fmt.Errorf(\"Couldn't resolve symlink for %v: %w\", source, err)\n}\n// after\nlink, err := os.Readlink(hostEntry)\nif err != nil {\n    if os.IsNotExist(err) {\n        continue // skip vanished symlink instead of failing the chroot\n    }\n    return fmt.Errorf(\"Couldn't resolve symlink for %v: %w\", source, err)\n}","handlingStrategy":"try-catch","validationCode":"// Go: pre-check the chroot source tree is traversable and links readable\nfunc readableSymlinks(root string) error {\n  return filepath.WalkDir(root, func(p string, d fs.DirEntry, err error) error {\n    if err != nil { return err }\n    if d.Type()&fs.ModeSymlink != 0 {\n      if _, err := os.Readlink(p); err != nil {\n        return fmt.Errorf(\"unreadable symlink %s: %w\", p, err)\n      }\n    }\n    return nil\n  })\n}","typeGuard":"func isSymlinkErr(err error) bool {\n  var le *os.LinkError\n  return errors.As(err, &le)\n}","tryCatchPattern":"if err := embedDirs(...); err != nil {\n  var le *os.LinkError\n  if errors.As(err, &le) && os.IsNotExist(le) {\n    // transient: skip/retry this entry\n  } else {\n    return err\n  }\n}","preventionTips":["Run the Nomad client as a user that can traverse all chroot-mapped host directories","Exclude volatile host paths (package-manager directories) from the chroot map","Monitor for broken symlinks on hosts included in the chroot map","Retry alloc placement once on transient embedDirs failures"],"tags":["filesystem","symlink","chroot","linux"],"backgroundTag":"symlink-resolution-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"}