neondatabase/neon · error
stripe size can't be given for unsharded tenants
Error message
stripe size can't be given for unsharded tenants
What it means
With exactly one URL, ShardSpec::new treats the tenant as unsharded (ShardCount 0). Unsharded tenants do not stripe keys, so no stripe size may be supplied. stripe_size=Some(_) together with a single URL is rejected.
Source
Thrown at pageserver/client_grpc/src/client.rs:406
/// 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.
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"));View on GitHub (pinned to 8f60b04da4)
Solutions
- Pass stripe_size=None when the map contains exactly one URL.
- Derive the stripe size from the shard count instead of an independent variable: None for one URL, Some(_) for more.
- Review spec deserialization for a serde default that materializes a stripe size where None was intended.
Example fix
// before let spec = ShardSpec::new(single_url_map, Some(ShardStripeSize(32768)))?; // -> "stripe size can't be given for unsharded tenants" // after let spec = ShardSpec::new(single_url_map, None)?;
Defensive patterns
Strategy: validation
Validate before calling
// unsharded (single URL) specs must pass None
let stripe_size = if urls.len() == 1 { None } else { Some(stripe) }; Type guard
fn stripe_size_valid(url_count: usize, stripe_size: Option<ShardStripeSize>) -> bool {
match url_count {
1 => stripe_size.is_none(),
2..=255 => stripe_size.is_some(),
_ => false,
}
} Prevention
- Normalize the stripe size to None whenever scaling a spec down to one shard.
- Avoid serde defaults that materialize a stripe size; make the field explicitly optional.
When it happens
Trigger: ShardSpec::new with exactly one URL and stripe_size == Some(_).
Common situations: Applying a default stripe size unconditionally when building specs; scaling a sharded tenant down to one shard without clearing the stripe size; copy-paste between sharded and unsharded spec builders.
Related errors
- stripe size must be given for sharded tenants
- shard {shard_index} missing from pageserver_connection_info
- must have at least one pageserver
- only unsharded tenants are supported at this time: {}
- can't reduce shard count from {} to {}
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/ae4cefb57bbf7787.
Report an issue: GitHub.