neondatabase/neon · error

Safekeeper set up for auth but no private key specified

Error message

Safekeeper set up for auth but no private key specified

What it means

storcon talks to safekeepers over their HTTP API. When any safekeeper in the local env has auth enabled, storcon must sign JWTs for those requests, which requires a private key; if the storage controller was configured without one, argument construction bails before the process is even started.

Source

Thrown at control_plane/src/storage_controller.rs:636

        if let Some(lag) = self.config.max_secondary_lag_bytes.as_ref() {
            args.push(format!("--max-secondary-lag-bytes={lag}"))
        }

        if let Some(threshold) = self.config.long_reconcile_threshold {
            args.push(format!(
                "--long-reconcile-threshold={}",
                humantime::Duration::from(threshold)
            ))
        }

        args.push(format!(
            "--neon-local-repo-dir={}",
            self.env.base_data_dir.display()
        ));

        if self.env.safekeepers.iter().any(|sk| sk.auth_enabled) && self.private_key.is_none() {
            anyhow::bail!("Safekeeper set up for auth but no private key specified");
        }

        if self.config.timelines_onto_safekeepers {
            args.push("--timelines-onto-safekeepers".to_string());
        }

        // neon_local is used in test environments where we often have less than 3 safekeepers.
        if self.config.timeline_safekeeper_count.is_some() || self.env.safekeepers.len() < 3 {
            let sk_cnt = self
                .config
                .timeline_safekeeper_count
                .unwrap_or(self.env.safekeepers.len());

            args.push(format!("--timeline-safekeeper-count={sk_cnt}"));
        }

        if let Some(duration) = self.config.shard_split_request_timeout {
            args.push(format!(

View on GitHub (pinned to 8f60b04da4)

Solutions

  1. Generate a JWT key pair and configure the storage controller's private key (config private_key / CLI key option)
  2. Or disable safekeeper auth in the env config for local development
Defensive patterns

Strategy: validation

Validate before calling

let auth_used = env.safekeepers.iter().any(|sk| sk.auth_enabled);
anyhow::ensure!(
    !auth_used || private_key.is_some(),
    "safekeeper auth enabled but the storage controller has no private key"
);

Prevention

When it happens

Trigger: The env config enables safekeeper auth (auth_enabled on any safekeeper) while the storage controller has no private key configured (missing private_key in config or on the CLI).

Common situations: Turning on JWT auth for safekeepers in a local env but forgetting to provision the storcon key pair, or reusing an old env config after auth support was introduced.

Related errors


AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16). Data as JSON: /api/errors/720fd34f03c10c36. Report an issue: GitHub.