netbirdio/netbird · error
failed to parse private root key: %w
Error message
failed to parse private root key: %w
What it means
reposign.ParseRootKey rejected the PEM bytes read from --root-private-key-file. ParseRootKey requires a single PEM block of the exact root-private-key type tag with no trailing PEM data and a valid unmarshalled Ed25519 key, so the file is not a root private key in the format this tool produces.
Source
Thrown at client/cmd/signer/artifactkey.go:107
if err := bundlePubKeysCmd.MarkFlagRequired("artifact-pub-key-file"); err != nil {
panic(fmt.Errorf("mark artifact-pub-key-file as required: %w", err))
}
if err := bundlePubKeysCmd.MarkFlagRequired("bundle-pub-key-file"); err != nil {
panic(fmt.Errorf("mark bundle-pub-key-file as required: %w", err))
}
}
func handleCreateArtifactKey(cmd *cobra.Command, rootPrivKeyFile, artifactPrivKeyFile, artifactPubKeyFile string, expiration time.Duration) error {
cmd.Println("Creating new artifact signing key...")
privKeyPEM, err := os.ReadFile(rootPrivKeyFile)
if err != nil {
return fmt.Errorf("read root private key file: %w", err)
}
privateRootKey, err := reposign.ParseRootKey(privKeyPEM)
if err != nil {
return fmt.Errorf("failed to parse private root key: %w", err)
}
artifactKey, privPEM, pubPEM, signature, err := reposign.GenerateArtifactKey(privateRootKey, expiration)
if err != nil {
return fmt.Errorf("generate artifact key: %w", err)
}
if err := os.WriteFile(artifactPrivKeyFile, privPEM, 0o600); err != nil {
return fmt.Errorf("write private key file (%s): %w", artifactPrivKeyFile, err)
}
if err := os.WriteFile(artifactPubKeyFile, pubPEM, 0o600); err != nil {
return fmt.Errorf("write public key file (%s): %w", artifactPubKeyFile, err)
}
signatureFile := artifactPubKeyFile + ".sig"
if err := os.WriteFile(signatureFile, signature, 0o600); err != nil {
return fmt.Errorf("write signature file (%s): %w", signatureFile, err)View on GitHub (pinned to 93e97f4bf1)
Solutions
- Use the private key file emitted by the create-root-key command, not the .pub companion and not an artifact key
- Inspect the PEM header line and confirm it matches the root private key type the tool generates
- If the key was produced by another tool, regenerate a root key with create-root-key and re-issue artifact keys
Defensive patterns
Strategy: validation
Validate before calling
// Confirm the PEM block type before invoking the CLI
block, _ := pem.Decode(data)
if block == nil || block.Type != rootPrivateKeyPEMType {
return fmt.Errorf("file is not a root private key PEM")
} Type guard
func isRootPrivateKeyPEM(data []byte) bool {
block, _ := pem.Decode(data)
return block != nil && block.Type == rootPrivateKeyPEMType
} Prevention
- Keep a naming convention that separates root vs artifact and priv vs pub files
- Never convert keys through external tools; use create-root-key output verbatim
- Verify the '-----BEGIN ...-----' header in CI before the signing step
When it happens
Trigger: Passing a public key instead of the private key; passing an artifact private key or a generic OpenSSH/openssl ed25519 key whose PEM header does not match the expected root type tag; a file with multiple concatenated PEM blocks; truncated or corrupted PEM.
Common situations: Mixing up the outputs of create-root-key (priv vs pub files); exporting a key with openssl and assuming compatibility; hand-editing or concatenating key files.
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 key: %w
- failed to decode PEM data
- generate artifact key: %w
- bundle artifact keys: %w
- failed to parse artifact private key: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/674f30d95f816992.
Report an issue: GitHub.