neondatabase/neon · error
invalid shard index {shard_id}, expected {count}
Error message
invalid shard index {shard_id}, expected {count} What it means
Every ShardIndex key in the map must carry a shard_count equal to the number of URLs supplied (0 when there is a single, unsharded URL). A key whose count differs, for example 8 URLs each labelled shard_count=4, cannot describe a coherent shard set and is rejected. The expected count is printed in the message.
Source
Thrown at pageserver/client_grpc/src/client.rs:413
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}"));
}
// The shard index' number and count must be consistent.
if !shard_id.is_unsharded() && shard_id.shard_number.0 >= shard_id.shard_count.0 {
return Err(anyhow!("invalid shard index {shard_id}"));
}
// The above conditions guarantee that we have all shards 0..count: len() matches count,
// shard number < count, and numbers are unique (via hashmap).
// Validate the URL.
if PageserverProtocol::from_connstring(url)? != PageserverProtocol::Grpc {
return Err(anyhow!("invalid shard URL {url}: must use gRPC"));
}
}
Ok(Self {
urls,
count,
stripe_size,View on GitHub (pinned to 8f60b04da4)
Solutions
- Rebuild the map so every ShardIndex uses ShardCount::new(urls.len() as u8), or 0 for a single URL.
- After any change to the shard set, regenerate all ShardIndex keys instead of patching individual entries.
- Read the error output: it shows the offending shard_id and the expected count, so compare number and count directly.
Example fix
// before: 8 URLs, but each ShardIndex still labelled with the old count of 4
let spec = ShardSpec::new(urls, stripe)?; // -> "invalid shard index <4:4>, expected 8"
// after: relabel keys for the new layout
let count = ShardCount::new(urls.len() as u8);
let urls = urls.into_iter()
.map(|(idx, url)| (ShardIndex::new(idx.shard_number.0 % count.0, count), url))
.collect::<HashMap<_, _>>();
let spec = ShardSpec::new(urls, stripe)?; Defensive patterns
Strategy: validation
Validate before calling
// every key's shard_count must equal the map size (0 for a single URL)
let expected = if urls.len() == 1 { ShardCount::new(0) } else { ShardCount::new(urls.len() as u8) };
for shard_id in urls.keys() {
anyhow::ensure!(
shard_id.shard_count == expected,
"shard index {shard_id} does not match expected count {expected}"
);
} Type guard
fn shard_indices_consistent(urls: &HashMap<ShardIndex, String>) -> bool {
let expected = if urls.len() == 1 { ShardCount::new(0) } else { ShardCount::new(urls.len() as u8) };
urls.keys().all(|k| k.shard_count == expected)
} Prevention
- Regenerate all ShardIndex keys whenever the shard set changes; never patch single entries.
- Build the map with (0..n).map(|i| (ShardIndex::new(i, count), url(i))) so labels cannot drift.
When it happens
Trigger: ShardSpec::new where any key's shard_id.shard_count differs from urls.len() (with 1 mapping to 0). Typical cause: adding or removing URLs without relabelling the indices, or mixing shards from two different shard layouts.
Common situations: A partially applied shard split where half the URLs still use the old count; constructing ShardIndex with the default unsharded count and forgetting to set shard_count.
Related errors
- too many shards: {n}
- only unsharded tenants are supported at this time: {}
- can't reduce shard count from {} to {}
- can't change stripe size from {} to {}
- no shards provided
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/926c01afcd737915.
Report an issue: GitHub.