{"record":{"id":"cf3fdd39a7d31827","repo":"juanfont/headscale","slug":"creating-directory-failed-with-permission-error-cf3fdd","errorCode":null,"errorMessage":"creating directory failed with permission error: %s","messagePattern":"creating directory failed with permission error: (.+?)","errorType":"console","errorClass":"ErrDirectoryPermission","httpStatus":null,"severity":"error","filePath":"hscontrol/util/file.go","lineNumber":56,"sourceCode":"}\n\nfunc GetFileMode(key string) fs.FileMode {\n\tmodeStr := viper.GetString(key)\n\n\tmode, err := strconv.ParseUint(modeStr, Base8, BitSize64)\n\tif err != nil {\n\t\treturn PermissionFallback\n\t}\n\n\treturn fs.FileMode(mode) //nolint:gosec // file mode is bounded by ParseUint\n}\n\nfunc EnsureDir(dir string) error {\n\tif _, err := os.Stat(dir); os.IsNotExist(err) { //nolint:noinlineerr\n\t\terr := os.MkdirAll(dir, PermissionFallback)\n\t\tif err != nil {\n\t\t\tif errors.Is(err, os.ErrPermission) {\n\t\t\t\treturn fmt.Errorf(\"%w: %s\", ErrDirectoryPermission, dir)\n\t\t\t}\n\n\t\t\treturn fmt.Errorf(\"creating directory %s: %w\", dir, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":38,"sourceCodeEnd":65,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/util/file.go#L38-L65","documentation":"Returned by util.EnsureDir when os.MkdirAll fails with a permission error while creating a directory (wrapped with ErrDirectoryPermission and the path). Headscale creates runtime directories (e.g. certificate/state dirs) at startup and fails fast when the filesystem denies writes.","triggerScenarios":"Calling EnsureDir on a path under a directory owned by root or another user while headscale runs unprivileged; read-only filesystems; containers where the volume is mounted with restrictive ownership.","commonSituations":"Running headscale as a non-root user for the first time against /var/lib/headscale created by root; Docker bind-mounts with wrong uid/gid; SELinux/AppArmor denials surfacing as EACCES.","solutions":["chown the directory tree to the user running headscale: sudo chown -R headscale:headscale /var/lib/headscale","Fix container volume permissions (match PUID/PGID or use a named volume)","Verify the filesystem is writable and not covered by SELinux/AppArmor policy"],"exampleFix":"# before\nsudo mkdir -p /var/lib/headscale && ls -ld /var/lib/headscale  # owned by root\n# after\nsudo mkdir -p /var/lib/headscale && sudo chown -R headscale:headscale /var/lib/headscale","handlingStrategy":"validation","validationCode":"import \"os\"\n\nfunc canCreateDir(dir string) error {\n    parent := filepath.Dir(dir)\n    if _, err := os.Stat(parent); err != nil {\n        return fmt.Errorf(\"parent %s missing: %w\", parent, err)\n    }\n    if err := unix.Access(parent, unix.W_OK); err != nil { // or attempt a temp file\n        return fmt.Errorf(\"no write permission on %s\", parent)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := util.EnsureDir(path); err != nil {\n    if errors.Is(err, util.ErrDirectoryPermission) {\n        // prompt to chown / fix volume, do not retry blindly\n    }\n    return err\n}","preventionTips":["Pre-create runtime directories in your unit spec / container image with correct ownership","Run headscale under a dedicated user and chown its data dirs at install time","Smoke-test directory writability in entrypoint scripts before exec'ing the server"],"tags":["filesystem","permissions","startup","container"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}