neondatabase/neon · error

no grpc_url for shard {shard_index}

Error message

no grpc_url for shard {shard_index}

What it means

Thrown by PageserverConnectionInfo::shard_url() in neon's compute_api crate when the caller asks for the Grpc protocol but the first PageserverShardConnectionInfo entry for that shard has grpc_url: None. Both libpq_url and grpc_url are Option<String> on the shard connection info, which is deserialized from control-plane JSON, so a pageserver that only registered a libpq URL will fail this lookup. It is an anyhow error (not a typed error), surfaced as a plain Err(String) to the compute startup code.

Source

Thrown at libs/compute_api/src/spec.rs:348

        };
        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_url
                .as_ref()
                .ok_or(anyhow::anyhow!("no libpq_url for shard {shard_index}"))?,
        };
        Ok(result)
    }
}

#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
pub struct PageserverShardInfo {
    pub pageservers: Vec<PageserverShardConnectionInfo>,
}

#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
pub struct PageserverShardConnectionInfo {
    pub id: Option<NodeId>,
    pub libpq_url: Option<String>,

View on GitHub (pinned to 8f60b04da4)

Solutions

  1. Check the shard's PageserverShardConnectionInfo and confirm grpc_url is populated; if the pageserver has no gRPC listener, call shard_url with PageserverProtocol::Libpq instead
  2. If you control the control-plane JSON, add the grpc:// URL for each pageserver so the field deserializes as Some(...)
  3. Fall back: try Grpc, and on this error retry the lookup with Libpq before giving up
  4. If you need per-pageserver selection or failover, iterate shard.pageservers yourself instead of using this first()-only convenience routine

Example fix

// before
let url = conn_info.shard_url(shard_number, PageserverProtocol::Grpc)?;

// after
let url = conn_info
    .shard_url(shard_number, PageserverProtocol::Grpc)
    .or_else(|_| conn_info.shard_url(shard_number, PageserverProtocol::Libpq))?;
Defensive patterns

Strategy: validation

Validate before calling

// Before calling shard_url with Grpc, confirm the field exists:
fn has_grpc_url(ci: &PageserverConnectionInfo, sn: ShardNumber) -> bool {
    let idx = ShardIndex { shard_number: sn, shard_count: ci.shard_count };
    ci.shards
        .get(&idx)
        .and_then(|s| s.pageservers.first())
        .and_then(|p| p.grpc_url.as_ref())
        .is_some()
}

Try / catch

// In Rust, treat it as a recoverable protocol-availability error:
match conn_info.shard_url(sn, PageserverProtocol::Grpc) {
    Ok(url) => { /* use grpc */ }
    Err(e) if e.to_string().contains("no grpc_url") => {
        let url = conn_info.shard_url(sn, PageserverProtocol::Libpq)?;
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling shard_url(shard_number, PageserverProtocol::Grpc) on a PageserverConnectionInfo whose shard map entry contains pageservers whose grpc_url field is null/absent. Happens when the control plane (e.g. neon_local / control_plane_builtin) only fills libpq_url for pageservers, but the compute was configured to prefer or explicitly use the gRPC protocol.

Common situations: Running a new compute against an older pageserver that does not expose a gRPC endpoint; mixed-version dev environments where the storage nodes never advertise grpc:// URLs; tests that hand-build PageserverShardConnectionInfo with only one of the two URL fields; toggling prefer_protocol to Grpc before the pageserver deployment actually publishes grpc_url.

Related errors


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