{"record":{"id":"f1d11fd3ca5aafdf","repo":"neondatabase/neon","slug":"safekeeper-sk-id-does-not-exist","errorCode":null,"errorMessage":"safekeeper {sk_id} does not exist","messagePattern":"safekeeper (.+?) does not exist","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"control_plane/src/endpoint.rs","lineNumber":680,"sourceCode":"            Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(\"\".to_string()),\n            Err(e) => Err(anyhow::Error::new(e).context(format!(\n                \"failed to read config file in {}\",\n                postgresql_conf_path.to_str().unwrap()\n            ))),\n        }\n    }\n\n    /// Map safekeepers ids to the actual connection strings.\n    fn build_safekeepers_connstrs(&self, sk_ids: Vec<NodeId>) -> Result<Vec<String>> {\n        let mut safekeeper_connstrings = Vec::new();\n        if self.mode == ComputeMode::Primary {\n            for sk_id in sk_ids {\n                let sk = self\n                    .env\n                    .safekeepers\n                    .iter()\n                    .find(|node| node.id == sk_id)\n                    .ok_or_else(|| anyhow!(\"safekeeper {sk_id} does not exist\"))?;\n                safekeeper_connstrings.push(format!(\"127.0.0.1:{}\", sk.get_compute_port()));\n            }\n        }\n        Ok(safekeeper_connstrings)\n    }\n\n    /// Generate a JWT with the correct claims.\n    pub fn generate_jwt(&self, scope: Option<ComputeClaimsScope>) -> Result<String> {\n        self.env.generate_auth_token(&ComputeClaims {\n            audience: match scope {\n                Some(ComputeClaimsScope::Admin) => Some(vec![COMPUTE_AUDIENCE.to_owned()]),\n                _ => None,\n            },\n            compute_id: match scope {\n                Some(ComputeClaimsScope::Admin) => None,\n                _ => Some(self.endpoint_id.clone()),\n            },\n            scope,","sourceCodeStart":662,"sourceCodeEnd":698,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/control_plane/src/endpoint.rs#L662-L698","documentation":"Endpoint::build_safekeepers_connstrs maps the safekeeper NodeIds supplied to an endpoint start (or reconfigure) to connection strings by looking each id up in the LocalEnv's safekeepers list. An id with no matching node in the env raises this error; it is a pure config-coherence failure, not a runtime/network one. Only Primary-mode endpoints resolve safekeeper ids, so read-replica starts with unknown ids pass through silently.","triggerScenarios":"Calling endpoint start with --safekeepers 3 when the env only has safekeepers 1 and 2; passing ids parsed from a spec/config file that were never created via `neon_local safekeeper create`; renaming/regenerating an env so previously valid ids no longer exist; reconfiguring an endpoint to a safekeeper set from a different env.","commonSituations":"Hand-editing scripts after adding/removing safekeepers; copy/pasting start commands between envs; automated harnesses deriving sk ids dynamically while safekeeper creation failed earlier in the script (so the list is shorter than expected).","solutions":["Check what exists: `neon_local safekeeper list` and compare the ids you pass to endpoint start.","Create the missing safekeeper (`neon_local safekeeper create --id <sk_id>`) before starting the endpoint.","Drop the unknown id from the endpoint's safekeeper list if it was stale.","If ids come from generated config, regenerate it after the safekeeper topology changes."],"exampleFix":"// before\nlet connstrs = endpoint.build_safekeepers_connstrs(vec![NodeId(3)])?; // safekeeper 3 does not exist\n\n// after\nlet valid: Vec<NodeId> = requested_ids.iter()\n    .copied()\n    .filter(|id| env.safekeepers.iter().any(|sk| sk.id == *id))\n    .collect();\nanyhow::ensure!(valid.len() == requested_ids.len(),\n    \"unknown safekeeper ids: {:?}\",\n    requested_ids.iter().filter(|id| !valid.contains(id)).collect::<Vec<_>>());\nlet connstrs = endpoint.build_safekeepers_connstrs(valid)?;","handlingStrategy":"validation","validationCode":"// resolve ids against the env before starting/reconfiguring the endpoint\nlet unknown: Vec<_> = sk_ids.iter()\n    .filter(|id| !env.safekeepers.iter().any(|sk| sk.id == **id))\n    .collect();\nanyhow::ensure!(unknown.is_empty(), \"unknown safekeeper ids: {unknown:?}\");","typeGuard":"fn all_safekeepers_exist(env: &LocalEnv, ids: &[NodeId]) -> bool {\n    ids.iter().all(|id| env.safekeepers.iter().any(|sk| sk.id == *id))\n}","tryCatchPattern":"match endpoint.start(args).await {\n    Err(e) if e.to_string().contains(\"safekeeper\") && e.to_string().contains(\"does not exist\") => {\n        // create missing safekeepers, then retry start\n        for id in missing_ids { create_safekeeper(env, id)?; }\n        endpoint.start(args).await\n    }\n    other => other,\n}","preventionTips":["Create all safekeepers before endpoints that reference them in setup scripts.","Derive endpoint safekeeper lists from `safekeeper list` output instead of hardcoding ids.","Fail fast in harnesses if safekeeper creation errored earlier in the script."],"tags":["rust","neon","control-plane","endpoint","safekeeper","config-validation"],"backgroundTag":"config-node-not-found","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}