neondatabase/neon · error

pageserver has less than limit_to_first_n_targets={limit} te

Error message

pageserver has less than limit_to_first_n_targets={limit} tenants

What it means

Shared target discovery truncates the sorted timeline list to limit_to_first_n_targets and then requires that at least that many timelines existed. When the pageserver exposes fewer timelines than the limit, the bench aborts with the requested limit in the message. This fires even for limit=1 on an empty list, so it usually precedes the caller's own empty-list checks.

Source

Thrown at pageserver/pagebench/src/util/cli/targets.rs:26

    pub(crate) limit_to_first_n_targets: Option<usize>,
    pub(crate) targets: Option<Vec<TenantTimelineId>>,
}

pub(crate) async fn discover(
    api_client: &Arc<mgmt_api::Client>,
    spec: Spec,
) -> anyhow::Result<Vec<TenantTimelineId>> {
    let mut timelines = if let Some(targets) = spec.targets {
        targets
    } else {
        mgmt_api::util::get_pageserver_tenant_timelines_unsharded(api_client).await?
    };

    if let Some(limit) = spec.limit_to_first_n_targets {
        timelines.sort(); // for determinism
        timelines.truncate(limit);
        if timelines.len() < limit {
            anyhow::bail!("pageserver has less than limit_to_first_n_targets={limit} tenants");
        }
    }

    info!("timelines:\n{:?}", timelines);
    info!("number of timelines:\n{:?}", timelines.len());

    Ok(timelines)
}

View on GitHub (pinned to 8f60b04da4)

Solutions

  1. Lower the limit to the number of timelines actually present; count them via the management API tenant list first
  2. Create or attach additional timelines if the bench genuinely needs N targets
  3. Pass explicit targets instead of a numeric limit where the bench allows it

Example fix

# before: server has 2 timelines
pagebench basebackup --limit-to-first-n-targets 4 ...
# after
pagebench basebackup --limit-to-first-n-targets 2 ...
Defensive patterns

Strategy: validation

Validate before calling

let available = mgmt_api::util::get_pageserver_tenant_timelines_unsharded(&client).await?;
let limit = spec.limit_to_first_n_targets.unwrap_or(available.len());
anyhow::ensure!(
    available.len() >= limit,
    "need {limit} timelines, found {}",
    available.len()
);

Try / catch

let timelines = match discover(&client, spec).await {
    Ok(ts) => ts,
    Err(e) if e.to_string().contains("limit_to_first_n_targets") => {
        eprintln!("lower --limit-to-first-n-targets or add timelines");
        return Err(e);
    }
    Err(e) => return Err(e),
};

Prevention

When it happens

Trigger: Passing --limit-to-first-n-targets N when the pageserver has fewer than N timelines; bench defaults (idle_streams uses 1) against a tenant-less pageserver; tenants deleted between discovery runs.

Common situations: Single-tenant dev pageservers used with multi-target bench profiles; stale scripts assuming old tenant counts; a wrong management API endpoint yielding an empty list that undershoots any limit of 1 or more.

Related errors


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