netbirdio/netbird · error
invalid key ID: %w
Error message
invalid key ID: %w
What it means
reposign.ParseKeyID rejected the --key-id value (client/internal/updater/reposign/key.go:51). A KeyID is the first 8 bytes of the SHA-256 of the Ed25519 public key, rendered as exactly 16 hex characters; create-root-key prints it as RootKey[ID=...]. The error is 'invalid KeyID length: got N, want 16 hex chars (8 bytes)' for any other length, or 'failed to decode KeyID' when non-hex characters are present.
Source
Thrown at client/cmd/signer/revocation.go:143
privateRootKey, err := reposign.ParseRootKey(privKeyPEM)
if err != nil {
return fmt.Errorf("failed to parse private root key: %w", err)
}
rlBytes, err := os.ReadFile(revocationListFile)
if err != nil {
return fmt.Errorf("failed to read revocation list file: %w", err)
}
rl, err := reposign.ParseRevocationList(rlBytes)
if err != nil {
return fmt.Errorf("failed to parse revocation list: %w", err)
}
kid, err := reposign.ParseKeyID(keyID)
if err != nil {
return fmt.Errorf("invalid key ID: %w", err)
}
newRLBytes, sigBytes, err := reposign.ExtendRevocationList(*privateRootKey, *rl, kid, expirationDuration)
if err != nil {
return fmt.Errorf("failed to extend revocation list: %w", err)
}
if err := writeOutputFiles(revocationListFile, revocationListFile+".sig", newRLBytes, sigBytes); err != nil {
return fmt.Errorf("failed to write output files: %w", err)
}
cmd.Println("✅ Revocation list extended successfully")
return nil
}
func handleVerifyRevocationList(cmd *cobra.Command, revocationListFile, signatureFile, publicRootKeyFile string) error {
// Read revocation list file
rlBytes, err := os.ReadFile(revocationListFile)View on GitHub (pinned to 93e97f4bf1)
Solutions
- Extract exactly 16 hex characters: from RootKey[ID=1a2b3c4d5e6f7083, ...] take 1a2b3c4d5e6f7083
- Strip any 0x prefix, surrounding quotes, and whitespace before passing the flag
- Validate the shape before running: echo -n "$KID" | grep -qE '^[0-9a-fA-F]{16}$'
- Cross-check against the id field inside the signer public key PEM's JSON body
Example fix
# before signer extend-revocation-list --key-id 0x1a2b3c4d5e6f7080 --revocation-list-file rl.json --private-root-key root.pem # error: invalid key ID: invalid KeyID length: got 19, want 16 hex chars (8 bytes) # after signer extend-revocation-list --key-id 1a2b3c4d5e6f7080 --revocation-list-file rl.json --private-root-key root.pem
Defensive patterns
Strategy: validation
Validate before calling
var keyIDRe = regexp.MustCompile(`^[0-9a-fA-F]{16}$`)
func normalizeKeyID(raw string) (string, error) {
s := strings.TrimSpace(raw)
s = strings.TrimPrefix(s, "0x")
if !keyIDRe.MatchString(s) {
return "", fmt.Errorf("key ID %q must be exactly 16 hex chars", raw)
}
return s, nil
}
// kid, err := normalizeKeyID(keyIDFlag)
// if err != nil { /* fail before calling the signer */ } Type guard
func isValidKeyID(s string) bool {
return regexp.MustCompile(`^[0-9a-fA-F]{16}$`).MatchString(strings.TrimSpace(s))
} Prevention
- Script key IDs through a regex gate: ^[0-9a-fA-F]{16}$
- Copy IDs only from the RootKey[ID=...] line or the public key's id field
- Trim shell-captured values to kill stray newlines and spaces
When it happens
Trigger: Passing the full 64-character SHA-256 hex; a 0x prefix; whitespace or a trailing newline picked up from terminal copy/paste; an odd-length string; uppercase is accepted but any non-hex character (g-z) is not.
Common situations: Copying the whole key fingerprint instead of the 16-char ID from the RootKey[...] line; scripting the flag from a variable that includes quotes or padding; a key ID from an incompatible system that uses different encodings.
Related errors
- --expiration must be a positive duration (e.g., 720h, 365d,
- at least one --artifact-pub-key-file must be provided
- failed to read revocation list file: %w
- failed to parse revocation list: %w
- failed to read signature file: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/7a75742a566cfd9e.
Report an issue: GitHub.