netbirdio/netbird · error

failed to read revocation list file: %w

Error message

failed to read revocation list file: %w

What it means

os.ReadFile on the --revocation-list-file path failed inside extend-revocation-list. Extend mutates an existing signed list (it adds the key ID and re-signs), so the file must already exist and be readable; the most common cause is that no list has been created yet.

Source

Thrown at client/cmd/signer/revocation.go:133

	cmd.Println("✅ Revocation list created successfully")
	return nil
}

func handleExtendRevocationList(cmd *cobra.Command, keyID, revocationListFile, privateRootKeyFile string) error {
	privKeyPEM, err := os.ReadFile(privateRootKeyFile)
	if err != nil {
		return fmt.Errorf("failed to read private root key file: %w", err)
	}

	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 {

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Create the initial list first: signer create-revocation-list --revocation-list-file <path> --private-root-key <key>
  2. Verify the path and its readability: ls -l <path>
  3. Fetch/restore the current list from wherever it is published before extending
  4. Never hand-write the JSON from scratch — create it with the tool so the companion .sig exists

Example fix

# before
signer extend-revocation-list --key-id 1a2b3c4d5e6f7080 --revocation-list-file rl.json --private-root-key root.pem
# error: failed to read revocation list file: open rl.json: no such file or directory

# after
signer create-revocation-list --revocation-list-file rl.json --private-root-key root.pem
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 listExists(path string) bool {
    info, err := os.Stat(path)
    return err == nil && !info.IsDir() && info.Mode().Perm()&0o400 != 0
}

// if !listExists(revocationListFile) {
//     // run create-revocation-list first, then extend
// }

Prevention

When it happens

Trigger: Running extend-revocation-list before ever running create-revocation-list; a typo in the path; a list file owned by another user or with no read permission; the path naming a directory.

Common situations: New signer setup where the operator skips the create step; CI job that extends a list never fetched from storage; list stored beside keys under root ownership while running as another user.

Related errors


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