netbirdio/netbird · error
failed to parse public root key: %w
Error message
failed to parse public root key: %w
What it means
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'.
Source
Thrown at client/cmd/signer/revocation.go:181
return fmt.Errorf("failed to read revocation list file: %w", err)
}
// Read signature file
sigBytes, err := os.ReadFile(signatureFile)
if err != nil {
return fmt.Errorf("failed to read signature file: %w", err)
}
// Read public root key file
pubKeyPEM, err := os.ReadFile(publicRootKeyFile)
if err != nil {
return fmt.Errorf("failed to read public root key file: %w", err)
}
// Parse public root key
publicKey, err := reposign.ParseRootPublicKey(pubKeyPEM)
if err != nil {
return fmt.Errorf("failed to parse public root key: %w", err)
}
// Parse signature
signature, err := reposign.ParseSignature(sigBytes)
if err != nil {
return fmt.Errorf("failed to parse signature: %w", err)
}
// Validate revocation list
rl, err := reposign.ValidateRevocationList([]reposign.PublicKey{publicKey}, rlBytes, *signature)
if err != nil {
return fmt.Errorf("failed to validate revocation list: %w", err)
}
// Display results
cmd.Println("✅ Revocation list signature is valid")
cmd.Printf("Last Updated: %s\n", rl.LastUpdated.Format(time.RFC3339))
cmd.Printf("Expires At: %s\n", rl.ExpiresAt.Format(time.RFC3339))View on GitHub (pinned to 93e97f4bf1)
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
Example fix
# before signer verify-revocation-list --revocation-list-file rl.json --signature-file rl.json.sig --public-root-key root-private.pem # error: failed to parse public root key: failed to parse root public key: PEM type is "ROOT PRIVATE KEY", want "ROOT PUBLIC KEY" # after signer verify-revocation-list --revocation-list-file rl.json --signature-file rl.json.sig --public-root-key root-public.pem
Defensive patterns
Strategy: type-guard
Validate before calling
func isRootPublicKeyPEM(data []byte) bool {
block, _ := pem.Decode(data)
return block != nil && block.Type == "ROOT PUBLIC KEY" && json.Valid(block.Bytes)
}
// pub, err := os.ReadFile(publicRootKeyFile)
// if err == nil && !isRootPublicKeyPEM(pub) { /* wrong key file */ } Type guard
func isRootPublicKeyPEM(data []byte) bool {
block, _ := pem.Decode(data)
return block != nil && block.Type == "ROOT PUBLIC KEY" && json.Valid(block.Bytes)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- failed to parse artifact private key: %w
- failed to parse private root key: %w
- failed to parse artifact key: %w
- failed to parse artifact public key: %w
- failed to parse root public key(s): %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/91bc8624e84d391b.
Report an issue: GitHub.