netbirdio/netbird · error
failed to decode PEM data
Error message
failed to decode PEM data
What it means
Returned by parsePublicKey (client/internal/updater/reposign/key.go:102) when pem.Decode cannot find a PEM block at the start of the data, returning nil. The reposign key format is a PEM block (with an expected type tag such as tagArtifactPublic) whose body is JSON describing the key, so input that is not PEM-armored cannot be a valid key and parsing aborts.
Source
Thrown at client/internal/updater/reposign/key.go:102
var keys []PublicKey
for len(bundle) > 0 {
keyInfo, rest, err := parsePublicKey(bundle, typeTag)
if err != nil {
return nil, err
}
keys = append(keys, keyInfo)
bundle = rest
}
if len(keys) == 0 {
return nil, errors.New("no keys found in bundle")
}
return keys, nil
}
func parsePublicKey(data []byte, typeTag string) (PublicKey, []byte, error) {
b, rest := pem.Decode(data)
if b == nil {
return PublicKey{}, nil, errors.New("failed to decode PEM data")
}
if b.Type != typeTag {
return PublicKey{}, nil, fmt.Errorf("PEM type is %q, want %q", b.Type, typeTag)
}
// Unmarshal JSON-embedded format
var pub PublicKey
if err := json.Unmarshal(b.Bytes, &pub); err != nil {
return PublicKey{}, nil, fmt.Errorf("failed to unmarshal public key: %w", err)
}
// Validate key length
if len(pub.Key) != ed25519.PublicKeySize {
return PublicKey{}, nil, fmt.Errorf("incorrect Ed25519 public key size: expected %d, got %d",
ed25519.PublicKeySize, len(pub.Key))
}
// Always recompute ID to ensure integrityView on GitHub (pinned to 93e97f4bf1)
Solutions
- Inspect the first bytes of the data: it must start with a valid PEM BEGIN line for the expected type tag.
- Re-fetch the key file from the canonical source rather than repairing it by hand.
- If generating bundles yourself, ensure each key is emitted with the exact PEM type tag expected by the caller (tagArtifactPublic / tagArtifactRoot).
Example fix
// before
block, _ := pem.Decode(rawKeyBytes) // raw JSON, block == nil -> error later
// after: verify armor before parsing
if _, rest := pem.Decode(data); len(rest) == len(data) {
return fmt.Errorf("input is not PEM-encoded")
}
key, rest, err := parsePublicKey(data, tagArtifactPublic) Defensive patterns
Strategy: try-catch
Validate before calling
// Guard before parsing: PEM data begins with a BEGIN line for the expected type.
if !bytes.HasPrefix(data, []byte("-----BEGIN "+expectedType+"-----")) {
return fmt.Errorf("key file is not a %s PEM block", expectedType)
} Try / catch
key, rest, err := parsePublicKey(data, typeTag)
if err != nil {
return nil, fmt.Errorf("parse public key bundle entry (expected %s PEM): %w", typeTag, err)
} Prevention
- Never re-encode or re-wrap reposign key files; move them as bytes.
- When generating bundles, emit keys with the exact PEM type tag the verifier expects.
When it happens
Trigger: Feeding parsePublicKey data that is raw JSON, base64, or binary without the -----BEGIN----- armor; a key file whose header line was corrupted or wrapped by a mail/transfer layer; an empty or whitespace-only remainder after previous keys in a multi-key bundle.
Common situations: Manual editing or re-encoding of key files strips the PEM armor; a transfer pipeline re-encodes line endings or truncates the BEGIN line; tests feeding ed25519 public keys in raw form instead of the reposign PEM+JSON envelope.
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
- no keys found in bundle
- failed to verify signature of artifact keys
- revocation list verification failed
- failed to parse private root key: %w
- failed to parse artifact key: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/fe986e53ef8c002c.
Report an issue: GitHub.