neondatabase/neon · critical

shard {shard_index} missing from pageserver_connection_info

Error message

shard {shard_index} missing from pageserver_connection_info shard map

What it means

When expanding a sharded pageserver connstring, compute_tools iterates shard numbers 0..shard_count and looks each up in conninfo.shards. For some shard index the map had no entry, so the per-shard pageserver list could not be built. The connstring's shard_count disagrees with the shard map it carries - malformed or truncated control-plane input. (The next line also has a hard expect on a non-empty pageservers list, a sibling failure mode.)

Source

Thrown at compute_tools/src/config.rs:94

                "# from compute spec's pageserver_connection_info.stripe_size field"
            )?;
            writeln!(file, "neon.stripe_size={stripe_size}")?;
        }

        let mut libpq_urls: Option<Vec<String>> = Some(Vec::new());
        let num_shards = if conninfo.shard_count.0 == 0 {
            1 // unsharded, treat it as a single shard
        } else {
            conninfo.shard_count.0
        };

        for shard_number in 0..num_shards {
            let shard_index = ShardIndex {
                shard_number: ShardNumber(shard_number),
                shard_count: conninfo.shard_count,
            };
            let info = conninfo.shards.get(&shard_index).ok_or_else(|| {
                anyhow::anyhow!(
                    "shard {shard_index} missing from pageserver_connection_info shard map"
                )
            })?;

            let first_pageserver = info
                .pageservers
                .first()
                .expect("must have at least one pageserver");

            // Add the libpq URL to the array, or if the URL is missing, reset the array
            // forgetting any previous entries. All servers must have a libpq URL, or none
            // at all.
            if let Some(url) = &first_pageserver.libpq_url {
                if let Some(ref mut urls) = libpq_urls {
                    urls.push(url.clone());
                }
            } else {
                libpq_urls = None

View on GitHub (pinned to 8f60b04da4)

Solutions

  1. Inspect the neon.pageserver_connstring GUC: the shard_count number must match the number of shard entries in the map
  2. Regenerate the connstring from the control plane after resharding completes so count and map are consistent
  3. For unsharded tenants keep shard_count at 0, which the code treats as a single shard and skips the map

Example fix

# before: shard_count=2 with only shard 0 mapped -> error
# after (GUC payload shape)
shard_count=2;shards={ 0: 'host=ps-0', 1: 'host=ps-1' }  # every index 0..count-1 present
Defensive patterns

Strategy: validation

Validate before calling

// Validate shard-map completeness before expanding the connstring
let n = if conninfo.shard_count.0 == 0 { 1 } else { conninfo.shard_count.0 };
for i in 0..n {
    let idx = ShardIndex { shard_number: ShardNumber(i), shard_count: conninfo.shard_count };
    let info = conninfo.shards.get(&idx).ok_or(anyhow!("shard {idx} missing"))?;
    if info.pageservers.is_empty() { anyhow::bail!("shard {idx} has no pageservers"); }
}

Type guard

fn shard_map_complete(conninfo: &PageserverConnectionInfo) -> bool {
    let n = if conninfo.shard_count.0 == 0 { 1 } else { conninfo.shard_count.0 };
    (0..n).all(|i| {
        let idx = ShardIndex { shard_number: ShardNumber(i), shard_count: conninfo.shard_count };
        conninfo.shards.get(&idx).map(|s| !s.pageservers.is_empty()).unwrap_or(false)
    })
}

Try / catch

// Config error at startup: surface which shard is missing instead of retrying
if let Some(missing) = (0..n).map(|i| /* build idx */).find(|idx| !conninfo.shards.contains_key(idx)) {
    return Err(anyhow!("incomplete shard map: missing shard {missing} of {}", conninfo.shard_count));
}

Prevention

When it happens

Trigger: PageserverConnectionInfo parsed with shard_count > 0 but whose shards map lacks an entry for one of the shard indices in 0..shard_count; e.g. a connstring built for N shards but embedding only N-1 shard entries.

Common situations: Control-plane bugs during shard resharding (shard_count bumped before shard map updated); hand-edited or templated connstrings; version skew in how shard maps are serialized into the GUC; partially migrated sharded tenants.

Related errors


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