netbirdio/netbird · error
failed to parse revocation list: %w
Error message
failed to parse revocation list: %w
What it means
reposign.ParseRevocationList failed on the optional revocation file. Beyond JSON unmarshalling, it enforces that last_updated and expires_at are present and non-zero, and that every key in the revoked map is a valid 16-hex-char KeyID (reposign/revocation.go:39-87). A missing field, a hand-edited key ID, or passing the .sig file fails.
Source
Thrown at client/cmd/signer/artifactsign.go:243
return fmt.Errorf("read signature file: %w", err)
}
signature, err := reposign.ParseSignature(sigBytes)
if err != nil {
return fmt.Errorf("failed to parse signature: %w", err)
}
// Read optional revocation list
var revocationList *reposign.RevocationList
if revocationFile != "" {
revData, err := os.ReadFile(revocationFile)
if err != nil {
return fmt.Errorf("read revocation file: %w", err)
}
revocationList, err = reposign.ParseRevocationList(revData)
if err != nil {
return fmt.Errorf("failed to parse revocation list: %w", err)
}
}
// Validate artifact key(s)
validKeys, err := reposign.ValidateArtifactKeys(rootPublicKeys, artifactKeyData, *signature, revocationList)
if err != nil {
return fmt.Errorf("artifact key verification failed: %w", err)
}
cmd.Println("✅ Artifact key(s) verified successfully")
cmd.Printf("Signed by root key: %s\n", signature.KeyID)
cmd.Printf("Signature timestamp: %s\n", signature.Timestamp.Format("2006-01-02 15:04:05 MST"))
cmd.Printf("\nValid artifact keys (%d):\n", len(validKeys))
for i, key := range validKeys {
cmd.Printf(" [%d] Key ID: %s\n", i+1, key.Metadata.ID)
cmd.Printf(" Created: %s\n", key.Metadata.CreatedAt.Format("2006-01-02 15:04:05 MST"))
if !key.Metadata.ExpiresAt.IsZero() {
cmd.Printf(" Expires: %s\n", key.Metadata.ExpiresAt.Format("2006-01-02 15:04:05 MST"))View on GitHub (pinned to 93e97f4bf1)
Solutions
- Validate shape: jq '.revoked, .last_updated, .expires_at' <file> must all be present, and revoked keys must be 16-char hex strings
- Only produce lists via `signer create-revocation-list` / `signer extend-revocation-list`
- Confirm you passed the list JSON, not its .sig
- Re-download the file if the download may be truncated
Example fix
// before (hand-edited list missing expires_at)
{"revoked":{"deadbeefdeadbeef":"2026-01-01T00:00:00Z"}}
// after (created by the signer, all fields present)
./signer create-revocation-list --revocation-list-file revocation-list.json --private-root-key root-key.pem Defensive patterns
Strategy: validation
Validate before calling
var probe struct {
Revoked map[string]time.Time `json:"revoked"`
LastUpdated time.Time `json:"last_updated"`
ExpiresAt time.Time `json:"expires_at"`
}
if err := json.Unmarshal(revData, &probe); err != nil || probe.LastUpdated.IsZero() || probe.ExpiresAt.IsZero() {
log.Fatalf("revocation list malformed: %v", err)
} Prevention
- Create and modify revocation lists only via create-revocation-list / extend-revocation-list
- Never hand-edit the JSON; missing last_updated or expires_at hard-fails parsing
When it happens
Trigger: Hand-written or trimmed revocation JSON missing expires_at or last_updated; a revoked map key that is not 16 hex chars; passing revocation-list.json.sig to --revocation-file; malformed JSON from a partial download.
Common situations: Ops edits the revocation list directly instead of using create/extend-revocation-list; the .sig and the list get swapped in scripts.
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
- sign artifact: %w
- failed to parse signature: %w
- connector type change not allowed
- service name is required
- service name exceeds maximum length of 255 characters
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/eabb98f35257bdb8.
Report an issue: GitHub.