neondatabase/neon · error
too many shards: {n}
Error message
too many shards: {n} What it means
ShardSpec::new in pageserver/client_grpc builds a tenant shard specification from a HashMap of ShardIndex to URL. The shard count is stored in a u8 (ShardCount), and 0 is reserved for unsharded tenants, so at most 255 shard URLs are accepted. A map with more than 255 entries is rejected with this error before any other validation runs.
Source
Thrown at pageserver/client_grpc/src/client.rs:397
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}"));
}
// The shard index' number and count must be consistent.View on GitHub (pinned to 8f60b04da4)
Solutions
- Reduce the shard set to 255 or fewer URLs; ShardCount is u8-backed with 0 reserved for unsharded, so 255 is a hard maximum.
- Inspect the code that builds the HashMap for duplicated entries or generation loops that run twice (e.g. 1..=N appended two times).
- If the tenant genuinely needs more than 255 shards, split the tenant or raise the limit upstream in ShardCount; never truncate the map to sneak past the check.
Example fix
// before let spec = ShardSpec::new(all_shard_urls, Some(stripe))?; // 300 entries -> "too many shards: 300" // after assert!(all_shard_urls.len() <= u8::MAX as usize, "max 255 shards"); let spec = ShardSpec::new(all_shard_urls, Some(stripe))?;
Defensive patterns
Strategy: validation
Validate before calling
// before calling ShardSpec::new
const MAX_SHARDS: usize = u8::MAX as usize; // 0 reserved for unsharded
anyhow::ensure!(
(1..=MAX_SHARDS).contains(&urls.len()),
"shard map has {} entries; 1..=255 required",
urls.len()
); Type guard
fn shard_count_representable(urls: &HashMap<ShardIndex, String>) -> bool {
(1..=u8::MAX as usize).contains(&urls.len())
} Try / catch
match ShardSpec::new(urls, stripe_size) {
Ok(spec) => spec,
Err(e) if e.to_string().starts_with("too many shards") => {
// reduce the shard set, then rebuild the spec with regenerated indices
return Err(e.context("shard set exceeds u8 ShardCount; split the tenant"));
}
Err(e) => return Err(e),
} Prevention
- Clamp or assert shard counts at the configuration boundary before building the map.
- Treat 255 as a documented product limit next to every shard-count knob.
- Generate shard maps programmatically instead of assembling them by hand.
When it happens
Trigger: Calling ShardSpec::new(urls, stripe_size), or any control-plane spec parser that feeds it, with urls.len() greater than 255 (u8::MAX). The count check runs first, so stripe size and shard index validation never execute.
Common situations: A test harness or control plane generates N shards from a config value without clamping N; a script duplicates shard URLs into an oversized map; an operator tries to scale a single tenant beyond the current 255-shard ceiling.
Related errors
- invalid shard index {shard_id}, expected {count}
- 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/7801269c0fe14bb3.
Report an issue: GitHub.