{"record":{"id":"9dc3396afa93591d","repo":"netbirdio/netbird","slug":"failed-to-parse-artifact-private-key-w","errorCode":null,"errorMessage":"failed to parse artifact private key: %w","messagePattern":"failed to parse artifact private key: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/cmd/signer/artifactsign.go","lineNumber":136,"sourceCode":"\tvar privKeyPEM []byte\n\tvar err error\n\n\tif envKey := os.Getenv(envArtifactPrivateKey); envKey != \"\" {\n\t\t// Use key from environment variable\n\t\tprivKeyPEM = []byte(envKey)\n\t} else if privKeyFile != \"\" {\n\t\t// Fall back to file\n\t\tprivKeyPEM, err = os.ReadFile(privKeyFile)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"read private key file: %w\", err)\n\t\t}\n\t} else {\n\t\treturn fmt.Errorf(\"artifact private key must be provided via %s environment variable or --artifact-key-file flag\", envArtifactPrivateKey)\n\t}\n\n\tprivateKey, err := reposign.ParseArtifactKey(privKeyPEM)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse artifact private key: %w\", err)\n\t}\n\n\tartifactData, err := os.ReadFile(artifactFile)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"read artifact file: %w\", err)\n\t}\n\n\tsignature, err := reposign.SignData(privateKey, artifactData)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"sign artifact: %w\", err)\n\t}\n\n\tsigFile := artifactFile + \".sig\"\n\tif err := os.WriteFile(artifactFile+\".sig\", signature, 0o600); err != nil {\n\t\treturn fmt.Errorf(\"write signature file (%s): %w\", sigFile, err)\n\t}\n\n\tcmd.Printf(\"✅ Artifact signed successfully.\\n\")","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/cmd/signer/artifactsign.go#L118-L154","documentation":"Thrown by `signer sign-artifact` when reposign.ParseArtifactKey cannot decode the key supplied via the NB_ARTIFACT_PRIV_KEY env var or --artifact-key-file. The parser expects a single custom PEM block of type \"ARTIFACT PRIVATE KEY\" whose payload is JSON ({key, id, created_at, expires_at}) with a 64-byte Ed25519 private key (reposign/key.go:131-156). It fails on PEM decode failure, trailing PEM data, a wrong PEM type tag, bad JSON, or a wrong key length.","triggerScenarios":"Passing a ROOT PRIVATE KEY or a standard OpenSSL/SSH PEM instead of an artifact key; newlines stripped from the PEM when it is stored as a single-line CI secret in NB_ARTIFACT_PRIV_KEY (pem.Decode then fails); concatenating two PEM blocks (trailing-data check); hand-editing the JSON body inside the PEM.","commonSituations":"CI secret manager flattens the multi-line PEM to one line; release engineer picks the root key from the vault instead of the artifact key; an editor appends a second key block to the file.","solutions":["Confirm the input is the artifact key produced by `signer generate-artifact-key`: first line must be -----BEGIN ARTIFACT PRIVATE KEY----- and the file must contain exactly one PEM block","If using NB_ARTIFACT_PRIV_KEY, export it preserving line breaks (e.g. NB_ARTIFACT_PRIV_KEY=\"$(cat artifact-key.pem)\") or switch to --artifact-key-file","Check the PEM body is intact: base64 lines unmodified and the embedded JSON parses (jq on the decoded block)","Regenerate the key pair with generate-artifact-key if the file is suspected corrupted"],"exampleFix":"// before\nNB_ARTIFACT_PRIV_KEY=LS0tLS1CRUdJTi... ./signer sign-artifact --artifact-file netbird\n\n// after\n./signer sign-artifact --artifact-key-file artifact-key.pem --artifact-file netbird","handlingStrategy":"type-guard","validationCode":"pemData, err := os.ReadFile(keyFile)\nif err != nil {\n    log.Fatalf(\"read key: %v\", err)\n}","typeGuard":"func isArtifactPrivateKeyPEM(data []byte) bool {\n    block, rest := pem.Decode(data)\n    return block != nil && block.Type == \"ARTIFACT PRIVATE KEY\" && len(rest) == 0\n}","tryCatchPattern":null,"preventionTips":["Only use keys produced by signer generate-artifact-key; never convert keys with openssl","Store the PEM as a file artifact in CI (secret file) instead of a flattened env var so line breaks survive","Name keys unambiguously: artifact-key-priv.pem vs root-key-priv.pem"],"tags":["go","cli","crypto","ed25519","pem","release-signing"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}