neondatabase/neon · error
invalid shard index {shard_id}
Error message
invalid shard index {shard_id} What it means
For a sharded ShardIndex, the shard number is 0-based and must be strictly less than the shard count. A key such as shard 4 of 4 (number >= count) is rejected here; the preceding check has already ensured the count itself matches urls.len().
Source
Thrown at pageserver/client_grpc/src/client.rs:417
};
// 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
- Use 0-based numbers: shard numbers must span 0..count, that is 0 to count-1.
- Generate keys programmatically: (0..n).map(|i| ShardIndex::new(i, ShardCount::new(n))).
- Check the shard_id in the message; its number must be smaller than its count.
Example fix
// before: 1-based numbering -> shard 8 of 8 is invalid
for i in 1..=n {
map.insert(ShardIndex::new(i, count), url(i));
}
// after: 0-based numbering
for i in 0..n {
map.insert(ShardIndex::new(i, count), url(i));
} Defensive patterns
Strategy: validation
Validate before calling
for shard_id in urls.keys() {
if !shard_id.is_unsharded() {
anyhow::ensure!(
(shard_id.shard_number.0 as usize) < urls.len(),
"shard number {} out of range for {} shards",
shard_id.shard_number.0,
urls.len()
);
}
} Type guard
fn shard_numbers_in_range(urls: &HashMap<ShardIndex, String>) -> bool {
urls.keys().all(|k| k.is_unsharded() || (k.shard_number.0 as usize) < urls.len())
} Prevention
- Always generate shard numbers with a 0-based range (0..n), never 1..=n.
- Validate numbering in one helper used by every code path that builds ShardIndex maps.
When it happens
Trigger: ShardSpec::new with any key where shard_number.0 >= shard_count.0 and the index is not the unsharded one. Most commonly produced by 1-based generation loops (for i in 1..=n).
Common situations: Generating shard indices with 1-based numbering; hand-written configs numbering shards 1..N; off-by-one after increasing the shard count.
Related errors
- 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
- too many shards: {n}
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/ca6b26f8056f5dbd.
Report an issue: GitHub.