{"record":{"id":"91bc8624e84d391b","repo":"netbirdio/netbird","slug":"failed-to-parse-public-root-key-w","errorCode":null,"errorMessage":"failed to parse public root key: %w","messagePattern":"failed to parse public root key: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/cmd/signer/revocation.go","lineNumber":181,"sourceCode":"\t\treturn fmt.Errorf(\"failed to read revocation list file: %w\", err)\n\t}\n\n\t// Read signature file\n\tsigBytes, err := os.ReadFile(signatureFile)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read signature file: %w\", err)\n\t}\n\n\t// Read public root key file\n\tpubKeyPEM, err := os.ReadFile(publicRootKeyFile)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read public root key file: %w\", err)\n\t}\n\n\t// Parse public root key\n\tpublicKey, err := reposign.ParseRootPublicKey(pubKeyPEM)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse public root key: %w\", err)\n\t}\n\n\t// Parse signature\n\tsignature, err := reposign.ParseSignature(sigBytes)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse signature: %w\", err)\n\t}\n\n\t// Validate revocation list\n\trl, err := reposign.ValidateRevocationList([]reposign.PublicKey{publicKey}, rlBytes, *signature)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to validate revocation list: %w\", err)\n\t}\n\n\t// Display results\n\tcmd.Println(\"✅ Revocation list signature is valid\")\n\tcmd.Printf(\"Last Updated: %s\\n\", rl.LastUpdated.Format(time.RFC3339))\n\tcmd.Printf(\"Expires At: %s\\n\", rl.ExpiresAt.Format(time.RFC3339))","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/cmd/signer/revocation.go#L163-L199","documentation":"reposign.ParseRootPublicKey rejected the PEM (root.go:41 delegating to key.go:99). It requires a PEM block whose type is exactly \"ROOT PUBLIC KEY\" and a JSON body with a 32-byte Ed25519 public key; it then recomputes the KeyID from the key bytes. Wrapped errors: 'failed to decode PEM data', 'PEM type is %q, want %q' (e.g. you passed the private key), 'failed to unmarshal public key', or 'incorrect Ed25519 public key size: expected 32, got N'.","triggerScenarios":"Passing the private root key file to --public-root-key (type is ROOT PRIVATE KEY); passing an artifact-signing public key instead of the root public key; a PEM corrupted or re-wrapped in transit; a key bundle with the wrong block type.","commonSituations":"Verifying with the wrong half of the pair after rotation; public keys distributed under generic names like key.pem; PEMs mangled by editors that wrap base64 lines.","solutions":["Read the wrapped error — the 'PEM type is ...' variant means the wrong key file was passed","Check the header: head -1 must show -----BEGIN ROOT PUBLIC KEY-----","Use the public half that create-root-key wrote alongside the private key","If size/unmarshal checks fail, redistribute the public key from a trusted copy; do not repair PEM bodies by hand"],"exampleFix":"# before\nsigner verify-revocation-list --revocation-list-file rl.json --signature-file rl.json.sig --public-root-key root-private.pem\n# error: failed to parse public root key: failed to parse root public key: PEM type is \"ROOT PRIVATE KEY\", want \"ROOT PUBLIC KEY\"\n\n# after\nsigner verify-revocation-list --revocation-list-file rl.json --signature-file rl.json.sig --public-root-key root-public.pem","handlingStrategy":"type-guard","validationCode":"func isRootPublicKeyPEM(data []byte) bool {\n    block, _ := pem.Decode(data)\n    return block != nil && block.Type == \"ROOT PUBLIC KEY\" && json.Valid(block.Bytes)\n}\n\n// pub, err := os.ReadFile(publicRootKeyFile)\n// if err == nil && !isRootPublicKeyPEM(pub) { /* wrong key file */ }","typeGuard":"func isRootPublicKeyPEM(data []byte) bool {\n    block, _ := pem.Decode(data)\n    return block != nil && block.Type == \"ROOT PUBLIC KEY\" && json.Valid(block.Bytes)\n}","tryCatchPattern":null,"preventionTips":["Name public and private key files unambiguously (root-public.pem / root-private.pem)","Check the PEM header line before passing the flag","During rotation, keep old and new keys in versioned directories, never overwriting names"],"tags":["go","cli","pem","ed25519","key-management"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}