{"record":{"id":"bc328e2b171044d1","repo":"hashicorp/nomad","slug":"couldn-t-create-symlink-w","errorCode":null,"errorMessage":"Couldn't create symlink: %w","messagePattern":"Couldn't create symlink: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/allocdir/task_dir.go","lineNumber":300,"sourceCode":"\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\n\t// Recurse on self to copy subdirectories.\n\tif len(subdirs) != 0 {\n\t\treturn t.embedDirs(subdirs)\n\t}\n\n\treturn nil","sourceCodeStart":282,"sourceCodeEnd":318,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/allocdir/task_dir.go#L282-L318","documentation":"After successfully reading a host symlink, embedDirs recreates it inside the task directory via os.Symlink. This error is returned when Symlink fails for any reason other than the link already existing (which is tolerated as 'symlinking twice'). It indicates the task-directory side of the chroot could not be populated.","triggerScenarios":"os.Symlink(link, taskEntry) fails with an error whose Err string is not 'file exists' — e.g. EACCES/EPERM (task dir not writable by the client user), ENAMETOOLONG, EPERM on filesystems forbidding symlinks, or a non-symlink file already occupying taskEntry.","commonSituations":"Task alloc dir permissions corrupted or owned by another user; allocating on a filesystem (some network mounts, Windows pre-DevMode) that disallows symlink creation; a leftover real file at the task path from a previous interrupted alloc.","solutions":["Inspect the wrapped error's Err value to learn the actual syscall failure (errno) for taskEntry","Verify the Nomad client user owns/is writable the alloc/task directory (check st_uid and mode of the data dir)","Remove the stale allocation directory so the chroot is rebuilt from scratch, then reschedule the alloc","Ensure the client data dir lives on a local filesystem that supports symlinks (avoid exotic network mounts)"],"exampleFix":"// before\nif err := os.Symlink(link, taskEntry); err != nil {\n    if err.(*os.LinkError).Err.Error() != \"file exists\" {\n        return fmt.Errorf(\"Couldn't create symlink: %w\", err)\n    }\n}\n// after\nif err := os.Symlink(link, taskEntry); err != nil {\n    if !errors.Is(err, os.ErrExist) {\n        return fmt.Errorf(\"Couldn't create symlink: %w\", err)\n    }\n}","handlingStrategy":"validation","validationCode":"// Ensure task dir is writable and symlink-capable before building chroot\nfi, err := os.Stat(taskDir)\nif err != nil || !fi.IsDir() { return fmt.Errorf(\"bad task dir\") }\nif err := os.Chmod(taskDir, fi.Mode()|0700); err != nil { return err }\ntmp := filepath.Join(taskDir, \".symlink_probe\")\nif err := os.Symlink(\".\", tmp); err != nil { return fmt.Errorf(\"symlinks unsupported: %w\", err) }\nos.Remove(tmp)","typeGuard":null,"tryCatchPattern":"if err != nil {\n  var le *os.LinkError\n  if errors.As(err, &le) && errors.Is(le, os.ErrExist) {\n    return nil // tolerated duplicate\n  }\n  return err\n}","preventionTips":["Keep the client data dir owned solely by the Nomad client user","Place data_dir on a local filesystem that supports symlinks (ext4/xfs), not network mounts","Clean stale alloc directories after crashes before re-allocating on the same node","After OS/user changes, verify the agent can write its data dir"],"tags":["filesystem","symlink","chroot","permissions"],"backgroundTag":"symlink-creation-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"}