{"record":{"id":"dec635f176d12139","repo":"netbirdio/netbird","slug":"write-public-key-file-s-w-dec635","errorCode":null,"errorMessage":"write public key file (%s): %w","messagePattern":"write public key file \\((.+?)\\): %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/cmd/signer/rootkey.go","lineNumber":68,"sourceCode":"\tif err := createRootKeyCmd.MarkFlagRequired(\"expiration\"); err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc handleGenerateRootKey(cmd *cobra.Command, privKeyFile, pubKeyFile string, expiration time.Duration) error {\n\trk, privPEM, pubPEM, err := reposign.GenerateRootKey(expiration)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"generate root key: %w\", err)\n\t}\n\n\t// Write private key\n\tif err := os.WriteFile(privKeyFile, privPEM, 0o600); err != nil {\n\t\treturn fmt.Errorf(\"write private key file (%s): %w\", privKeyFile, err)\n\t}\n\n\t// Write public key\n\tif err := os.WriteFile(pubKeyFile, pubPEM, 0o600); err != nil {\n\t\treturn fmt.Errorf(\"write public key file (%s): %w\", pubKeyFile, err)\n\t}\n\n\tcmd.Printf(\"%s\\n\\n\", rk.String())\n\tcmd.Printf(\"✅ Root key pair generated successfully.\\n\")\n\treturn nil\n}\n","sourceCodeStart":50,"sourceCodeEnd":75,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/cmd/signer/rootkey.go#L50-L75","documentation":"Returned by the signer CLI's create-root-key command when os.WriteFile cannot persist the generated public key PEM to the path given with --pub-key-file (mode 0600). It wraps the underlying *fs.PathError. Note the private key file is written before this step, so hitting this error leaves a freshly written private key on disk with no matching public key beside it.","triggerScenarios":"Running `signer create-root-key` where the --pub-key-file path is in a missing/unwritable directory, on a read-only filesystem, with no space left, or where the target is a directory or an unwritable existing file — while the --priv-key-file path is writable.","commonSituations":"The two flags pointing at different directories where only the private key's directory was prepared; typos in the public key path; CI artifacts directory not created before the step runs; disk filling up between the two writes.","solutions":["Verify the parent directory of --pub-key-file exists and is writable (mkdir -p); the tool does not create directories.","Keep both key files in the same prepared directory so one permission fix covers both writes.","Free disk space if ENOSPC, or remount the volume read-write if EROFS.","If a partial run left a private key behind, delete it before re-running so you do not accumulate an orphaned key."],"exampleFix":"# before\nsigner create-root-key --priv-key-file ./keys/root.priv --pub-key-file /etc/signer/root.pub --expiration 8760h\n# -> write public key file (/etc/signer/root.pub): open ...: no such file or directory\n\n# after\nmkdir -p ./keys\nsigner create-root-key --priv-key-file ./keys/root.priv --pub-key-file ./keys/root.pub --expiration 8760h","handlingStrategy":"validation","validationCode":"// ensure BOTH output paths are writable before generating anything\npaths := []string{privKeyFile, pubKeyFile}\nfor _, p := range paths {\n\tif dir := filepath.Dir(p); dir != \"\" {\n\t\tif err := os.MkdirAll(dir, 0o755); err != nil {\n\t\t\tlog.Fatalf(\"prepare dir for %s: %v\", p, err)\n\t\t}\n\t}\n\tf, err := os.OpenFile(p, os.O_WRONLY|os.O_CREATE, 0o600)\n\tif err != nil {\n\t\tlog.Fatalf(\"path not writable: %s (%v)\", p, err)\n\t}\n\tf.Close()\n}","typeGuard":null,"tryCatchPattern":"if err := handleGenerateRootKey(cmd, priv, pub, exp); err != nil {\n\tvar pathErr *fs.PathError\n\tif errors.As(err, &pathErr) {\n\t\t// both key writes produce *fs.PathError; distinguish ENOSPC/EROFS/EACCES\n\t\t// and remember the priv file may exist from a partial run\n\t}\n}","preventionTips":["Keep --priv-key-file and --pub-key-file in the same directory you prepared — one permission fix then covers both writes.","Free disk space check before key ceremonies (signing keys are small, but full disks break atomic-looking flows).","After any failed run, ls both paths and remove orphans so a later success never mixes keys from different runs.","In CI, create the artifacts directory in a setup step rather than relying on the signer to imply it."],"tags":["go","cli","filesystem","permissions","signing"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}