{"record":{"id":"c952e667dc70c041","repo":"neondatabase/neon","slug":"must-have-at-least-one-pageserver-c952e6","errorCode":null,"errorMessage":"must have at least one pageserver","messagePattern":"must have at least one pageserver","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"libs/compute_api/src/spec.rs","lineNumber":342,"sourceCode":"        shard_number: ShardNumber,\n        protocol: PageserverProtocol,\n    ) -> anyhow::Result<&str> {\n        let shard_index = ShardIndex {\n            shard_number,\n            shard_count: self.shard_count,\n        };\n        let shard = self.shards.get(&shard_index).ok_or_else(|| {\n            anyhow::anyhow!(\"shard connection info missing for shard {}\", shard_index)\n        })?;\n\n        // Just use the first pageserver in the list. That's good enough for this\n        // convenience routine; if you need more control, like round robin policy or\n        // failover support, roll your own. (As of this writing, we never have more than\n        // one pageserver per shard anyway, but that will change in the future.)\n        let pageserver = shard\n            .pageservers\n            .first()\n            .ok_or(anyhow::anyhow!(\"must have at least one pageserver\"))?;\n\n        let result = match protocol {\n            PageserverProtocol::Grpc => pageserver\n                .grpc_url\n                .as_ref()\n                .ok_or(anyhow::anyhow!(\"no grpc_url for shard {shard_index}\"))?,\n            PageserverProtocol::Libpq => pageserver\n                .libpq_url\n                .as_ref()\n                .ok_or(anyhow::anyhow!(\"no libpq_url for shard {shard_index}\"))?,\n        };\n        Ok(result)\n    }\n}\n\n#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]\npub struct PageserverShardInfo {\n    pub pageservers: Vec<PageserverShardConnectionInfo>,","sourceCodeStart":324,"sourceCodeEnd":360,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/libs/compute_api/src/spec.rs#L324-L360","documentation":"After shard_url finds the shard's entry, it takes the first pageserver in that shard's pageservers list as the connection target. If the list is empty there is nothing to connect to, and this error fires. The docs note multi-pageserver shards are rare today, so in practice the list is built with exactly one element from the legacy connstr path.","triggerScenarios":"A PageserverShardInfo whose pageservers Vec is empty — possible when pageserver_connection_info JSON is hand-authored or generated with an empty pageservers array for some shard — and shard_url is called for that shard.","commonSituations":"Manually editing or generating ComputeSpec pageserver_connection_info and leaving a shard's pageservers empty; programmatic builders that skip pushing connection info under a branch; partial JSON merge dropping shard entries' contents.","solutions":["Fix the source spec so every shard in pageserver_connection_info has at least one pageserver entry (with libpq_url and/or grpc_url set)","Validate the parsed info after deserialization: every shard in 0..shard_count exists and has a non-empty pageservers list","If generating specs programmatically, assert on construction that pageservers is never empty"],"exampleFix":"// before\nshards: { shard_idx: PageserverShardInfo { pageservers: vec![], ... } }\n// after\nshards: { shard_idx: PageserverShardInfo { pageservers: vec![PageserverShardConnectionInfo { id: None, libpq_url: Some(url), grpc_url: None }], ... } }","handlingStrategy":"validation","validationCode":"fn validate(info: &PageserverConnectionInfo) -> anyhow::Result<()> {\n    for (idx, shard) in &info.shards {\n        anyhow::ensure!(!shard.pageservers.is_empty(),\n            \"shard {idx} has no pageservers\");\n        for ps in &shard.pageservers {\n            anyhow::ensure!(ps.libpq_url.is_some() || ps.grpc_url.is_some(),\n                \"shard {idx} pageserver has no URL\");\n        }\n    }\n    Ok(())\n}","typeGuard":"fn shard_has_pageserver(info: &PageserverConnectionInfo, idx: ShardIndex) -> bool {\n    info.shards.get(&idx).map(|s| !s.pageservers.is_empty()).unwrap_or(false)\n}","tryCatchPattern":null,"preventionTips":["Validate deserialized pageserver_connection_info in one place before use","In spec generators, push at least one connection entry per shard unconditionally","Prefer libpq_url plus grpc_url both set so protocol preference never starves"],"tags":["compute-api","pageserver","sharding","configuration","rust"],"backgroundTag":"no-pageserver-configured","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}