{"record":{"id":"12a0a0d76f3d519a","repo":"netbirdio/netbird","slug":"failed-to-parse-private-root-key-w-12a0a0","errorCode":null,"errorMessage":"failed to parse private root key: %w","messagePattern":"failed to parse private root key: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/cmd/signer/revocation.go","lineNumber":104,"sourceCode":"\t\tpanic(err)\n\t}\n\tif err := verifyRevocationListCmd.MarkFlagRequired(\"signature-file\"); err != nil {\n\t\tpanic(err)\n\t}\n\tif err := verifyRevocationListCmd.MarkFlagRequired(\"public-root-key\"); err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc handleCreateRevocationList(cmd *cobra.Command, revocationListFile string, privateRootKeyFile string) error {\n\tprivKeyPEM, err := os.ReadFile(privateRootKeyFile)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read private root key file: %w\", err)\n\t}\n\n\tprivateRootKey, err := reposign.ParseRootKey(privKeyPEM)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse private root key: %w\", err)\n\t}\n\n\trlBytes, sigBytes, err := reposign.CreateRevocationList(*privateRootKey, expirationDuration)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create revocation list: %w\", err)\n\t}\n\n\tif err := writeOutputFiles(revocationListFile, revocationListFile+\".sig\", rlBytes, sigBytes); err != nil {\n\t\treturn fmt.Errorf(\"failed to write output files: %w\", err)\n\t}\n\n\tcmd.Println(\"✅ Revocation list created successfully\")\n\treturn nil\n}\n\nfunc handleExtendRevocationList(cmd *cobra.Command, keyID, revocationListFile, privateRootKeyFile string) error {\n\tprivKeyPEM, err := os.ReadFile(privateRootKeyFile)\n\tif err != nil {","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/cmd/signer/revocation.go#L86-L122","documentation":"reposign.ParseRootKey failed in create-revocation-list: it expects a single PEM block of type \"ROOT PRIVATE KEY\" whose payload is JSON with a 64-byte Ed25519 private key (reposign/root.go:32-38, key.go:131-156). It rejects wrong PEM tags (passing the public root key or an artifact private key), trailing PEM data, undecodable PEM, bad JSON, and wrong key length.","triggerScenarios":"Passing the ROOT PUBLIC KEY file (most common — same name, different suffix); passing the ARTIFACT PRIVATE KEY; PEM mangled when copied through a terminal or chat (lost newlines); two concatenated PEM blocks.","commonSituations":"Key vault stores root-key.pem (public) and root-key-priv.pem side by side and the wrong one is selected; key copied with newlines collapsed; key pasted with trailing content.","solutions":["Check head -1 of the file: it must read -----BEGIN ROOT PRIVATE KEY-----","Confirm it is the private half of the root key pair from `signer generate-root-key`, not the public half and not the artifact key","Ensure the PEM block structure survived copying (64-column base64 lines, single block)","Regenerate the root key pair only as a last resort — it invalidates the whole existing chain of trust"],"exampleFix":"// before\n./signer create-revocation-list --private-root-key root-key-pub.pem ...\n\n// after\n./signer create-revocation-list --private-root-key root-key-priv.pem ...","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func isRootPrivateKeyPEM(data []byte) bool {\n    block, rest := pem.Decode(data)\n    return block != nil && block.Type == \"ROOT PRIVATE KEY\" && len(rest) == 0\n}","tryCatchPattern":null,"preventionTips":["Check the BEGIN line before every use: ROOT PRIVATE KEY, not ROOT PUBLIC KEY or ARTIFACT PRIVATE KEY","Transfer keys only as files through the secret store; never paste PEM text through terminals or chat","Keep exactly one PEM block per key file"],"tags":["go","crypto","ed25519","pem","release-signing","secrets"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}