netbirdio/netbird · error
failed to parse root public key(s): %w
Error message
failed to parse root public key(s): %w
What it means
parseRootPublicKeys failed: it calls reposign.ParseRootPublicKey, which requires a PEM block of type "ROOT PUBLIC KEY" with a JSON body and a 32-byte Ed25519 key (reposign/root.go:41-47, key.go:99-124). Wrong tag (most commonly an ARTIFACT PUBLIC KEY passed to --root-key-file), bad PEM, corrupt JSON, or wrong key length fails. Note the helper parses only the first block despite the plural "key(s)" in the message (artifactsign.go:270-276).
Source
Thrown at client/cmd/signer/artifactsign.go:219
func handleVerifyArtifactKey(cmd *cobra.Command, artifactKeyFile, rootKeyFile, signatureFile, revocationFile string) error {
cmd.Println("🔍 Verifying artifact key...")
// Read artifact key data
artifactKeyData, err := os.ReadFile(artifactKeyFile)
if err != nil {
return fmt.Errorf("read artifact key file: %w", err)
}
// Read root public key(s)
rootKeyData, err := os.ReadFile(rootKeyFile)
if err != nil {
return fmt.Errorf("read root key file: %w", err)
}
rootPublicKeys, err := parseRootPublicKeys(rootKeyData)
if err != nil {
return fmt.Errorf("failed to parse root public key(s): %w", err)
}
// Read signature
sigBytes, err := os.ReadFile(signatureFile)
if err != nil {
return fmt.Errorf("read signature file: %w", err)
}
signature, err := reposign.ParseSignature(sigBytes)
if err != nil {
return fmt.Errorf("failed to parse signature: %w", err)
}
// Read optional revocation list
var revocationList *reposign.RevocationList
if revocationFile != "" {
revData, err := os.ReadFile(revocationFile)
if err != nil {View on GitHub (pinned to 93e97f4bf1)
Solutions
- Pass the ROOT PUBLIC KEY PEM produced by `signer generate-root-key`: head -1 must read -----BEGIN ROOT PUBLIC KEY-----
- Confirm you did not swap --root-key-file and --artifact-key-file
- If using concatenated root keys, ensure the first PEM block is intact and correctly tagged
Example fix
// before ./signer verify-artifact-key --root-key-file artifact-key-pub.pem ... // after ./signer verify-artifact-key --root-key-file root-key-pub.pem ...
Defensive patterns
Strategy: type-guard
Type guard
func isRootPublicKeyPEM(data []byte) bool {
block, _ := pem.Decode(data)
return block != nil && block.Type == "ROOT PUBLIC KEY"
} Prevention
- Check the PEM tag line before passing a file as --root-key-file
- Keep artifact public keys and root public keys in separately named directories
When it happens
Trigger: Passing the artifact public key bundle to --root-key-file; passing a root private key; a standard PEM public key; first block of a concatenated root key bundle being corrupt.
Common situations: Two key files with similar names in the release directory; rotating root keys and grabbing the wrong generation; using openssl-generated keys outside the signer toolchain.
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 artifact public key: %w
- failed to parse private root key: %w
- sign artifact: %w
- artifact verification failed: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/eb954f81b121336f.
Report an issue: GitHub.