netbirdio/netbird · error
failed to parse signature: %w
Error message
failed to parse signature: %w
What it means
reposign.ParseSignature failed (signature.go:17): it is a plain json.Unmarshal into the Signature struct {signature []byte, timestamp time.Time, key_id KeyID, algorithm, hash_algo}. Failure means the .sig bytes are not valid JSON, have wrong field types, or carry a key_id string that the KeyID unmarshaler rejects (not 16 hex chars). Note json.Unmarshal ignores unknown and missing fields, so passing the revocation list itself as --signature-file parses successfully into zero values and blows up later in ValidateRevocationList with timestamp errors instead.
Source
Thrown at client/cmd/signer/revocation.go:187
return fmt.Errorf("failed to read signature file: %w", err)
}
// Read public root key file
pubKeyPEM, err := os.ReadFile(publicRootKeyFile)
if err != nil {
return fmt.Errorf("failed to read public root key file: %w", err)
}
// Parse public root key
publicKey, err := reposign.ParseRootPublicKey(pubKeyPEM)
if err != nil {
return fmt.Errorf("failed to parse public root key: %w", err)
}
// Parse signature
signature, err := reposign.ParseSignature(sigBytes)
if err != nil {
return fmt.Errorf("failed to parse signature: %w", err)
}
// Validate revocation list
rl, err := reposign.ValidateRevocationList([]reposign.PublicKey{publicKey}, rlBytes, *signature)
if err != nil {
return fmt.Errorf("failed to validate revocation list: %w", err)
}
// Display results
cmd.Println("✅ Revocation list signature is valid")
cmd.Printf("Last Updated: %s\n", rl.LastUpdated.Format(time.RFC3339))
cmd.Printf("Expires At: %s\n", rl.ExpiresAt.Format(time.RFC3339))
cmd.Printf("Number of revoked keys: %d\n", len(rl.Revoked))
if len(rl.Revoked) > 0 {
cmd.Println("\nRevoked Keys:")
for keyID, revokedTime := range rl.Revoked {
cmd.Printf(" - %s (revoked at: %s)\n", keyID, revokedTime.Format(time.RFC3339))View on GitHub (pinned to 93e97f4bf1)
Solutions
- Check the file is JSON and has the expected shape: jq '{signature, timestamp, key_id}' rl.json.sig
- If corrupted, regenerate the pair by re-running create/extend-revocation-list with the root private key
- Ensure the key_id value is exactly 16 hex characters
- Never hand-edit signature files — they are only meaningful as produced by the signer
Example fix
# before: rl.json.sig contains an HTML error page jq . rl.json.sig # parse error # after: regenerate the matched pair signer extend-revocation-list --key-id 1a2b3c4d5e6f7080 --revocation-list-file rl.json --private-root-key root.pem
Defensive patterns
Strategy: validation
Validate before calling
func looksLikeSignature(data []byte) bool {
var v map[string]json.RawMessage
return json.Unmarshal(data, &v) == nil &&
len(v["signature"]) > 0 &&
len(v["timestamp"]) > 0 &&
len(v["key_id"]) > 0
}
// sig, err := os.ReadFile(signatureFile)
// if err == nil && !looksLikeSignature(sig) { /* wrong or corrupt .sig */ } Type guard
func isSignatureJSON(data []byte) bool {
var v struct {
Signature json.RawMessage `json:"signature"`
Timestamp json.RawMessage `json:"timestamp"`
KeyID json.RawMessage `json:"key_id"`
}
return json.Unmarshal(data, &v) == nil &&
len(v.Signature) > 0 && len(v.Timestamp) > 0 && len(v.KeyID) > 0
} Prevention
- jq-validate .sig files after every transfer: jq 'keys' rl.json.sig
- Never hand-craft or re-serialize signature files
- Detect silent mix-ups early: a list passed as .sig parses to zero values and only fails later — validate field presence up front
When it happens
Trigger: A binary or garbage .sig file; a signature JSON edited so base64 or timestamps no longer unmarshal; a key_id field rewritten to a non-16-hex string; feeding the list JSON into --signature-file (fails downstream, not here).
Common situations: Signature file corrupted in transfer (CRLF mangling, HTML error page saved as .sig); hand-crafted signature files; tooling that re-serializes JSON with different field types.
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
- failed to create revocation list: %w
- failed to parse revocation list: %w
- failed to extend revocation list: %w
- failed to write output files: %w
- failed to read revocation list file: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/795ccc739146c827.
Report an issue: GitHub.