neondatabase/neon · error

no shards provided

Error message

no shards provided

What it means

`ShardSpec::new` derives the shard count from the number of URL entries (`urls.len()`), where 1 entry means unsharded and each shard needs its own pageserver address. An empty map has no meaningful count, so construction is rejected immediately with this error.

Solutions

  1. Populate the map with at least one shard URL before constructing the spec (one entry = unsharded)
  2. Check upstream filtering/discovery that produced the empty shard list (tenant id, labels, config)
  3. Default to the unsharded entry (ShardIndex::unsharded()) when the tenant has no shards yet
  4. Fail early in config loading if the shard URL list is empty, with a clearer message

Example fix

// before: empty map
let spec = ShardSpec::new(HashMap::new(), None)?; // no shards provided

// after: unsharded single-shard spec
let mut urls = HashMap::new();
urls.insert(ShardIndex::unsharded(), pageserver_url.to_string());
let spec = ShardSpec::new(urls, None)?;
Defensive patterns

Strategy: validation

Validate before calling

if urls.is_empty() {
    anyhow::bail!("cannot build ShardSpec: no pageserver URLs configured for tenant");
}
let spec = ShardSpec::new(urls, stripe_size)?;

Type guard

fn has_shard_urls(urls: &HashMap<ShardIndex, String>) -> bool {
    !urls.is_empty()
}

Prevention

When it happens

Trigger: Constructing a `ShardSpec` with an empty `HashMap<ShardIndex, String>` — e.g. iterating a filtered shard list that yielded nothing, or wiring up config before any pageserver addresses are known.

Common situations: Empty configuration/stub lists in tests; discovery code that filters out all shards (wrong tenant filter); building a client before the controller has assigned pageservers.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16). Data as JSON: /api/errors/609439936d154ec0. Report an issue: GitHub.

Appendix: source

Thrown at pageserver/client_grpc/src/client.rs:395

    ///
    /// NB: this is 0 for unsharded tenants, following `ShardIndex::unsharded()` convention.
    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}"));

View on GitHub (pinned to 8f60b04da4)