neondatabase/neon · error

Tenant shard not found

Error message

Tenant shard not found

What it means

watch_tenant_shard fetched the tenant description (GET control/v1/tenant/{tenant_id}) successfully, but no entry in desc.shards has a tenant_shard_id equal to the requested one. The tenant exists, but the specific shard (tenant id + shard number + count) does not. The command bails with this anyhow error from the Option::ok_or.

Source

Thrown at control_plane/storcon_cli/src/main.rs:1449

            "Waiting for tenant shard {tenant_shard_id} to be migrated to node {until_migrated_to}"
        );
    }

    loop {
        let desc = storcon_client
            .dispatch::<(), TenantDescribeResponse>(
                Method::GET,
                format!("control/v1/tenant/{}", tenant_shard_id.tenant_id),
                None,
            )
            .await?;

        // Output the current state of the tenant shard
        let shard = desc
            .shards
            .iter()
            .find(|s| s.tenant_shard_id == tenant_shard_id)
            .ok_or(anyhow::anyhow!("Tenant shard not found"))?;
        let summary = format!(
            "attached: {} secondary: {} {}",
            shard
                .node_attached
                .map(|n| format!("{n}"))
                .unwrap_or("none".to_string()),
            shard
                .node_secondary
                .iter()
                .map(|n| n.to_string())
                .collect::<Vec<_>>()
                .join(","),
            if shard.is_reconciling {
                "(reconciler active)"
            } else {
                "(reconciler idle)"
            }
        );

View on GitHub (pinned to 8f60b04da4)

Solutions

  1. Describe the tenant and list its actual shards (their shard numbers and shard count) to get a valid tenant_shard_id
  2. For an unsharded tenant, use shard number 0 with the unsharded count (e.g. <tenant_id>-0000 style id)
  3. Re-check the tenant_shard_id for typos and confirm it comes from the same storage controller

Example fix

# before: tenant is unsharded, only shard 0 exists
storcon_cli tenant-shard-watch --tenant-shard-id <tenant_id>-0002-8
# after
storcon_cli tenant-shard-watch --tenant-shard-id <tenant_id>-0000
Defensive patterns

Strategy: validation

Validate before calling

// Validate the tenant_shard_id against the describe response before watching
let desc: TenantDescribeResponse = client
    .dispatch(Method::GET, format!("control/v1/tenant/{}", tenant_shard_id.tenant_id), None)
    .await?;
anyhow::ensure!(
    desc.shards.iter().any(|s| s.tenant_shard_id == *tenant_shard_id),
    "shard {} not present; available shards: {:?}",
    tenant_shard_id,
    desc.shards.iter().map(|s| s.tenant_shard_id.to_string()).collect::<Vec<_>>()
);

Type guard

fn shard_exists(desc: &TenantDescribeResponse, id: &TenantShardId) -> bool {
    desc.shards.iter().any(|s| s.tenant_shard_id == *id)
}

Prevention

When it happens

Trigger: Passing a TenantShardId whose shard number/count does not match the tenant's actual sharding — for example shard 1 of an unsharded tenant that only has shard 0, or a stale shard count after the tenant was resharded (split/merged) — to tenant-shard-watch or tenant-shard-describe.

Common situations: Watching a shard with a hardcoded id after the tenant's shard count changed; assuming a tenant is sharded when it is unsharded (only shard 0000 exists); copy-pasting a tenant_shard_id from another environment; the shard was detached or merged away between listing and watching.

Related errors


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