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

  1. Use the private key file emitted by the create-root-key command, not the .pub companion and not an artifact key
  2. Inspect the PEM header line and confirm it matches the root private key type the tool generates
  3. 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

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

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/674f30d95f816992. Report an issue: GitHub.