{"record":{"id":"f6902bee06d1ebd9","repo":"juanfont/headscale","slug":"reading-policy-file-w","errorCode":null,"errorMessage":"reading policy file: %w","messagePattern":"reading policy file: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/headscale/cli/policy.go","lineNumber":132,"sourceCode":"\t\tfmt.Println(policyData)\n\n\t\treturn nil\n\t},\n}\n\nvar setPolicy = &cobra.Command{\n\tUse:   \"set\",\n\tShort: \"Updates the ACL Policy\",\n\tLong: `\n\tUpdates the existing ACL Policy with the provided policy. The policy must be a valid HuJSON object.\n\tThis command only works when the acl.policy_mode is set to \"db\", and the policy will be stored in the database.`,\n\tAliases: []string{\"put\", \"update\"},\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\tpolicyPath, _ := cmd.Flags().GetString(\"file\")\n\n\t\tpolicyBytes, err := os.ReadFile(policyPath)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"reading policy file: %w\", err)\n\t\t}\n\n\t\tif bypass, _ := cmd.Flags().GetBool(bypassFlag); bypass {\n\t\t\td, err := openBypassDB(cmd)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer d.Close()\n\n\t\t\tusers, err := d.ListUsers(nil)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"loading users for policy validation: %w\", err)\n\t\t\t}\n\n\t\t\t_, err = policy.NewPolicyManager(policyBytes, users, views.Slice[types.NodeView]{})\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"parsing policy file: %w\", err)\n\t\t\t}","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/cmd/headscale/cli/policy.go#L114-L150","documentation":"Thrown by `headscale policy set` when os.ReadFile on the --file flag value fails. The policy HuJSON file cannot be read before any validation or upload happens. The wrapped error is a standard os.PathError naming the exact reason (no such file, permission denied).","triggerScenarios":"`headscale policy set --file ./policy.hujson` where the path is wrong, a directory, or unreadable; tilde (~) not expanded because the flag value is passed to os.ReadFile literally.","commonSituations":"Relative path used from a different working directory; file created as root and CLI run as another user; typo in filename; using ~/policy.hujson unexpanded in a script.","solutions":["Check the path in the error's wrapped PathError — use an absolute path.","Verify readability: `ls -l` and `cat` the file as the same user running the CLI.","In scripts, expand the path ($HOME/policy.hujson) instead of using ~."],"exampleFix":"# before\nheadscale policy set --file ~/policy.hujson\n\n# after\nheadscale policy set --file \"$HOME/policy.hujson\"","handlingStrategy":"validation","validationCode":"func checkPolicyFile(path string) ([]byte, error) {\n    if path == \"\" {\n        return nil, fmt.Errorf(\"--file is required\")\n    }\n    abs, err := filepath.Abs(path)\n    if err != nil {\n        return nil, err\n    }\n    fi, err := os.Stat(abs)\n    if err != nil {\n        return nil, fmt.Errorf(\"policy file missing: %w\", err)\n    }\n    if fi.IsDir() {\n        return nil, fmt.Errorf(\"policy path is a directory: %s\", abs)\n    }\n    return os.ReadFile(abs)\n}","typeGuard":null,"tryCatchPattern":"policyBytes, err := os.ReadFile(absPath)\nif err != nil {\n    if errors.Is(err, os.ErrNotExist) {\n        // typo in path: list candidate files, fix path\n    } else if errors.Is(err, os.ErrPermission) {\n        // fix ownership/run as file owner\n    }\n    return err\n}","preventionTips":["Use absolute paths in automation; expand $HOME explicitly.","In CI, assert the policy file exists as a lint step before calling the CLI.","Keep policy files next to the repo that versions them."],"tags":["filesystem","cli","policy"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}