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

  1. Extract exactly 16 hex characters: from RootKey[ID=1a2b3c4d5e6f7083, ...] take 1a2b3c4d5e6f7083
  2. Strip any 0x prefix, surrounding quotes, and whitespace before passing the flag
  3. Validate the shape before running: echo -n "$KID" | grep -qE '^[0-9a-fA-F]{16}$'
  4. 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

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


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/7a75742a566cfd9e. Report an issue: GitHub.