{"record":{"id":"609439936d154ec0","repo":"neondatabase/neon","slug":"no-shards-provided","errorCode":null,"errorMessage":"no shards provided","messagePattern":"no shards provided","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pageserver/client_grpc/src/client.rs","lineNumber":395,"sourceCode":"    ///\n    /// NB: this is 0 for unsharded tenants, following `ShardIndex::unsharded()` convention.\n    count: ShardCount,\n    /// The stripe size for these shards.\n    ///\n    /// INVARIANT: None for unsharded tenants, Some for sharded.\n    stripe_size: Option<ShardStripeSize>,\n}\n\nimpl ShardSpec {\n    /// Creates a new shard spec with the given URLs and stripe size. All shards must be given.\n    /// The stripe size must be Some for sharded tenants, or None for unsharded tenants.\n    pub fn new(\n        urls: HashMap<ShardIndex, String>,\n        stripe_size: Option<ShardStripeSize>,\n    ) -> anyhow::Result<Self> {\n        // Compute the shard count.\n        let count = match urls.len() {\n            0 => return Err(anyhow!(\"no shards provided\")),\n            1 => ShardCount::new(0), // NB: unsharded tenants use 0, like `ShardIndex::unsharded()`\n            n if n > u8::MAX as usize => return Err(anyhow!(\"too many shards: {n}\")),\n            n => ShardCount::new(n as u8),\n        };\n\n        // Validate the stripe size.\n        if stripe_size.is_none() && !count.is_unsharded() {\n            return Err(anyhow!(\"stripe size must be given for sharded tenants\"));\n        }\n        if stripe_size.is_some() && count.is_unsharded() {\n            return Err(anyhow!(\"stripe size can't be given for unsharded tenants\"));\n        }\n\n        // Validate the shard spec.\n        for (shard_id, url) in &urls {\n            // The shard index must match the computed shard count, even for unsharded tenants.\n            if shard_id.shard_count != count {\n                return Err(anyhow!(\"invalid shard index {shard_id}, expected {count}\"));","sourceCodeStart":377,"sourceCodeEnd":413,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/pageserver/client_grpc/src/client.rs#L377-L413","documentation":"`ShardSpec::new` derives the shard count from the number of URL entries (`urls.len()`), where 1 entry means unsharded and each shard needs its own pageserver address. An empty map has no meaningful count, so construction is rejected immediately with this error.","triggerScenarios":"Constructing a `ShardSpec` with an empty `HashMap<ShardIndex, String>` — e.g. iterating a filtered shard list that yielded nothing, or wiring up config before any pageserver addresses are known.","commonSituations":"Empty configuration/stub lists in tests; discovery code that filters out all shards (wrong tenant filter); building a client before the controller has assigned pageservers.","solutions":["Populate the map with at least one shard URL before constructing the spec (one entry = unsharded)","Check upstream filtering/discovery that produced the empty shard list (tenant id, labels, config)","Default to the unsharded entry (ShardIndex::unsharded()) when the tenant has no shards yet","Fail early in config loading if the shard URL list is empty, with a clearer message"],"exampleFix":"// before: empty map\nlet spec = ShardSpec::new(HashMap::new(), None)?; // no shards provided\n\n// after: unsharded single-shard spec\nlet mut urls = HashMap::new();\nurls.insert(ShardIndex::unsharded(), pageserver_url.to_string());\nlet spec = ShardSpec::new(urls, None)?;","handlingStrategy":"validation","validationCode":"if urls.is_empty() {\n    anyhow::bail!(\"cannot build ShardSpec: no pageserver URLs configured for tenant\");\n}\nlet spec = ShardSpec::new(urls, stripe_size)?;","typeGuard":"fn has_shard_urls(urls: &HashMap<ShardIndex, String>) -> bool {\n    !urls.is_empty()\n}","tryCatchPattern":null,"preventionTips":["Fail early in config loading when the shard URL list is empty, with a clearer message","Default to a single unsharded entry (ShardIndex::unsharded()) when no shards are assigned yet","Check tenant filters/discovery queries that can silently produce empty shard lists"],"tags":["rust","neon","sharding","pageserver","grpc-client","validation"],"backgroundTag":"missing-required-parameter","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}