{"record":{"id":"fe986e53ef8c002c","repo":"netbirdio/netbird","slug":"failed-to-decode-pem-data","errorCode":null,"errorMessage":"failed to decode PEM data","messagePattern":"failed to decode PEM data","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/internal/updater/reposign/key.go","lineNumber":102,"sourceCode":"\tvar keys []PublicKey\n\tfor len(bundle) > 0 {\n\t\tkeyInfo, rest, err := parsePublicKey(bundle, typeTag)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tkeys = append(keys, keyInfo)\n\t\tbundle = rest\n\t}\n\tif len(keys) == 0 {\n\t\treturn nil, errors.New(\"no keys found in bundle\")\n\t}\n\treturn keys, nil\n}\n\nfunc parsePublicKey(data []byte, typeTag string) (PublicKey, []byte, error) {\n\tb, rest := pem.Decode(data)\n\tif b == nil {\n\t\treturn PublicKey{}, nil, errors.New(\"failed to decode PEM data\")\n\t}\n\tif b.Type != typeTag {\n\t\treturn PublicKey{}, nil, fmt.Errorf(\"PEM type is %q, want %q\", b.Type, typeTag)\n\t}\n\n\t// Unmarshal JSON-embedded format\n\tvar pub PublicKey\n\tif err := json.Unmarshal(b.Bytes, &pub); err != nil {\n\t\treturn PublicKey{}, nil, fmt.Errorf(\"failed to unmarshal public key: %w\", err)\n\t}\n\n\t// Validate key length\n\tif len(pub.Key) != ed25519.PublicKeySize {\n\t\treturn PublicKey{}, nil, fmt.Errorf(\"incorrect Ed25519 public key size: expected %d, got %d\",\n\t\t\ted25519.PublicKeySize, len(pub.Key))\n\t}\n\n\t// Always recompute ID to ensure integrity","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/internal/updater/reposign/key.go#L84-L120","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before\nblock, _ := pem.Decode(rawKeyBytes) // raw JSON, block == nil -> error later\n\n// after: verify armor before parsing\nif _, rest := pem.Decode(data); len(rest) == len(data) {\n    return fmt.Errorf(\"input is not PEM-encoded\")\n}\nkey, rest, err := parsePublicKey(data, tagArtifactPublic)","handlingStrategy":"try-catch","validationCode":"// Guard before parsing: PEM data begins with a BEGIN line for the expected type.\nif !bytes.HasPrefix(data, []byte(\"-----BEGIN \"+expectedType+\"-----\")) {\n    return fmt.Errorf(\"key file is not a %s PEM block\", expectedType)\n}","typeGuard":null,"tryCatchPattern":"key, rest, err := parsePublicKey(data, typeTag)\nif err != nil {\n    return nil, fmt.Errorf(\"parse public key bundle entry (expected %s PEM): %w\", typeTag, err)\n}","preventionTips":["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."],"tags":["go","netbird","security","pem","parsing","keys"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}