neondatabase/neon · error
no shards provided
Error message
no shards provided
What it means
`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.
Source
Thrown at pageserver/client_grpc/src/client.rs:395
///
/// NB: this is 0 for unsharded tenants, following `ShardIndex::unsharded()` convention.
count: ShardCount,
/// The stripe size for these shards.
///
/// INVARIANT: None for unsharded tenants, Some for sharded.
stripe_size: Option<ShardStripeSize>,
}
impl ShardSpec {
/// Creates a new shard spec with the given URLs and stripe size. All shards must be given.
/// The stripe size must be Some for sharded tenants, or None for unsharded tenants.
pub fn new(
urls: HashMap<ShardIndex, String>,
stripe_size: Option<ShardStripeSize>,
) -> anyhow::Result<Self> {
// Compute the shard count.
let count = match urls.len() {
0 => return Err(anyhow!("no shards provided")),
1 => ShardCount::new(0), // NB: unsharded tenants use 0, like `ShardIndex::unsharded()`
n if n > u8::MAX as usize => return Err(anyhow!("too many shards: {n}")),
n => ShardCount::new(n as u8),
};
// Validate the stripe size.
if stripe_size.is_none() && !count.is_unsharded() {
return Err(anyhow!("stripe size must be given for sharded tenants"));
}
if stripe_size.is_some() && count.is_unsharded() {
return Err(anyhow!("stripe size can't be given for unsharded tenants"));
}
// Validate the shard spec.
for (shard_id, url) in &urls {
// The shard index must match the computed shard count, even for unsharded tenants.
if shard_id.shard_count != count {
return Err(anyhow!("invalid shard index {shard_id}, expected {count}"));View on GitHub (pinned to 8f60b04da4)
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
Example fix
// before: empty map let spec = ShardSpec::new(HashMap::new(), None)?; // no shards provided // after: unsharded single-shard spec let mut urls = HashMap::new(); urls.insert(ShardIndex::unsharded(), pageserver_url.to_string()); let spec = ShardSpec::new(urls, None)?;
Defensive patterns
Strategy: validation
Validate before calling
if urls.is_empty() {
anyhow::bail!("cannot build ShardSpec: no pageserver URLs configured for tenant");
}
let spec = ShardSpec::new(urls, stripe_size)?; Type guard
fn has_shard_urls(urls: &HashMap<ShardIndex, String>) -> bool {
!urls.is_empty()
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- can't reduce shard count from {} to {}
- can't change stripe size from {} to {}
- only unsharded tenants are supported at this time: {}
- too many shards: {n}
- stripe size must be given for sharded tenants
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/609439936d154ec0.
Report an issue: GitHub.