netbirdio/netbird · error

failed to generate root key: %w

Error message

failed to generate root key: %w

What it means

Umbrella wrap around handleGenerateRootKey (rootkey.go:31). It covers three distinct inner failures, each self-identifying in the %w chain: 'generate root key: ...' from reposign.GenerateRootKey (entropy or marshal failure), 'write private key file (path): ...' and 'write public key file (path): ...' from the two os.WriteFile calls with 0600 permissions. The dominant real-world cause is one of the writes failing on a missing directory or insufficient permissions.

Source

Thrown at client/cmd/signer/rootkey.go:32

	privKeyFile    string
	pubKeyFile     string
	rootExpiration time.Duration
)

var createRootKeyCmd = &cobra.Command{
	Use:          "create-root-key",
	Short:        "Create a new root key pair",
	Long:         `Create a new root key pair and specify an expiration time for it.`,
	SilenceUsage: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		// Validate expiration
		if rootExpiration <= 0 {
			return fmt.Errorf("--expiration must be a positive duration (e.g., 720h, 365d, 8760h)")
		}

		// Run main logic
		if err := handleGenerateRootKey(cmd, privKeyFile, pubKeyFile, rootExpiration); err != nil {
			return fmt.Errorf("failed to generate root key: %w", err)
		}
		return nil
	},
}

func init() {
	rootCmd.AddCommand(createRootKeyCmd)
	createRootKeyCmd.Flags().StringVar(&privKeyFile, "priv-key-file", "", "Path to output private key file")
	createRootKeyCmd.Flags().StringVar(&pubKeyFile, "pub-key-file", "", "Path to output public key file")
	createRootKeyCmd.Flags().DurationVar(&rootExpiration, "expiration", 0, "Expiration time for the root key (e.g., 720h,)")

	if err := createRootKeyCmd.MarkFlagRequired("priv-key-file"); err != nil {
		panic(err)
	}
	if err := createRootKeyCmd.MarkFlagRequired("pub-key-file"); err != nil {
		panic(err)
	}
	if err := createRootKeyCmd.MarkFlagRequired("expiration"); err != nil {

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Identify the failing step from the inner message (generate vs write private vs write public)
  2. mkdir -p the parent directories of both --priv-key-file and --pub-key-file
  3. Fix ownership of pre-existing target files or choose fresh paths
  4. If the inner error is 'generate root key', see the entropy guidance for error 359

Example fix

# before
signer create-root-key --priv-key-file /etc/netbird/keys/root.pem --pub-key-file /etc/netbird/keys/root-public.pem --expiration 8760h
# error: failed to generate root key: write private key file (/etc/netbird/keys/root.pem): open ...: no such file or directory

# after
mkdir -p /etc/netbird/keys
signer create-root-key --priv-key-file /etc/netbird/keys/root.pem --pub-key-file /etc/netbird/keys/root-public.pem --expiration 8760h
Defensive patterns

Strategy: validation

Validate before calling

func preflightKeygen(priv, pub string) error {
    for _, p := range []string{priv, pub} {
        dir := filepath.Dir(p)
        if _, err := os.Stat(dir); err != nil {
            return fmt.Errorf("output dir %s: %w", dir, err)
        }
        if err := ensureWritableDir(p); err != nil {
            return err
        }
    }
    return nil
}

// before create-root-key:
// err := preflightKeygen(privKeyFile, pubKeyFile)

Prevention

When it happens

Trigger: create-root-key with an output path in a directory that does not exist or is not writable; an existing file at the target owned by another user; in rare cases a system where the entropy source is unavailable so key generation itself fails.

Common situations: First-run setup on a new signing host where the key directory was never created; running as non-root against /etc paths; leftover root-owned key files from a previous attempt under a different user.

Related errors


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