neondatabase/neon · error
shard connection info missing for shard {}
Error message
shard connection info missing for shard {} What it means
shard_url looks up the shard's connection info in the shards HashMap keyed by ShardIndex (shard_number + shard_count). If no entry exists for that exact index — typically because the requested shard number is out of range for the configured shard_count — the lookup fails and this error reports the missing index.
Source
Thrown at libs/compute_api/src/spec.rs:332
shards,
prefer_protocol: PageserverProtocol::Libpq,
})
}
}
}
/// Convenience routine to get the connection string for a shard.
pub fn shard_url(
&self,
shard_number: ShardNumber,
protocol: PageserverProtocol,
) -> anyhow::Result<&str> {
let shard_index = ShardIndex {
shard_number,
shard_count: self.shard_count,
};
let shard = self.shards.get(&shard_index).ok_or_else(|| {
anyhow::anyhow!("shard connection info missing for shard {}", shard_index)
})?;
// Just use the first pageserver in the list. That's good enough for this
// convenience routine; if you need more control, like round robin policy or
// failover support, roll your own. (As of this writing, we never have more than
// one pageserver per shard anyway, but that will change in the future.)
let pageserver = shard
.pageservers
.first()
.ok_or(anyhow::anyhow!("must have at least one pageserver"))?;
let result = match protocol {
PageserverProtocol::Grpc => pageserver
.grpc_url
.as_ref()
.ok_or(anyhow::anyhow!("no grpc_url for shard {shard_index}"))?,
PageserverProtocol::Libpq => pageserver
.libpq_urlView on GitHub (pinned to 8f60b04da4)
Solutions
- Iterate shard numbers in 0..shard_count (from PageserverConnectionInfo.shard_count) instead of a hardcoded or stale bound
- Re-fetch/rebuild the connection info so its shard_count matches the current topology before resolving shard URLs
- Validate the requested shard number against shard_count before calling shard_url and surface a clearer error
Example fix
// before
for shard in 0..8 {
let url = conn_info.shard_url(ShardNumber(shard), protocol)?;
}
// after
for shard in 0..conn_info.shard_count.0 {
let url = conn_info.shard_url(ShardNumber(shard), protocol)?;
} Defensive patterns
Strategy: type-guard
Validate before calling
let idx = ShardIndex { shard_number, shard_count: conn_info.shard_count };
if !conn_info.shards.contains_key(&idx) {
anyhow::bail!("shard {idx} missing (have {:?}); refresh connection info",
conn_info.shards.keys().collect::<Vec<_>>());
} Type guard
fn has_shard(info: &PageserverConnectionInfo, shard_number: ShardNumber) -> bool {
let idx = ShardIndex { shard_number, shard_count: info.shard_count };
info.shards.contains_key(&idx)
} Prevention
- Derive loop bounds from conn_info.shard_count, never from external constants
- After any resharding, rebuild PageserverConnectionInfo before resolving shard URLs
- Validate on load that shards covers exactly 0..shard_count
When it happens
Trigger: Calling shard_url(shard_number, protocol) with a shard number >= shard_count, a shard number from a different (re)sharded generation, or an index built with a different shard_count than the one the map was populated with (from_connstr numbers shards 0..n-1).
Common situations: Iterating shard numbers from stale configuration after the tenant was split into more shards; mixing ShardIndex values from two different specs; off-by-one loops over shard numbers; requesting a shard after resharding changed the count.
Related errors
- {n} shards but no stripe_size
- must have at least one pageserver
- shard {shard_index} missing from pageserver_connection_info
- invalid compute claims scope "{s}"
- empty connection string
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/57af2147e76dab7a.
Report an issue: GitHub.