gitbutlerapp/gitbutler · error · anyhow::Error
Stopped listing GitLab jobs for pipeline {} after unsafe pag
Error message
Stopped listing GitLab jobs for pipeline {} after unsafe pagination state What it means
A loop-safety bail inside list_pipeline_jobs_for_ref's pagination over GET /projects/:id/pipelines/:id/jobs?per_page=100&page=N. It fires when either guard trips: pages_iterated reaches MAX_PIPELINE_JOB_PAGES (25 pages × 100 jobs = 2500 jobs), or the X-NextPage header (read by next_page_from_headers) yields a page string already seen — i.e., the server is cycling, which would loop forever. This is a deliberate fail-fast against GitLab or an intermediary misbehaving, and against unbounded memory on monster pipelines.
Source
Thrown at crates/but-gitlab/src/client.rs:672
.json()
.await
.with_context(|| format!("Failed to parse GitLab pipeline for ref '{reference}'"))?;
let pipeline_web_url = pipeline.web_url;
let pipeline_status = Some(pipeline.status);
let jobs_url = format!(
"{}/projects/{}/pipelines/{}/jobs",
self.base_url, project_id, pipeline.id
);
let mut jobs = Vec::new();
let mut next_page = Some("1".to_string());
let mut seen_pages = HashSet::new();
let mut pages_iterated = 0;
while let Some(page) = next_page.take() {
if pages_iterated >= MAX_PIPELINE_JOB_PAGES || !seen_pages.insert(page.clone()) {
bail!(
"Stopped listing GitLab jobs for pipeline {} after unsafe pagination state",
pipeline.id
);
}
pages_iterated += 1;
let response = self
.client
.get(&jobs_url)
.query(&[("per_page", "100"), ("page", page.as_str())])
.send()
.await
.with_context(|| {
format!("Failed to list GitLab jobs for pipeline {}", pipeline.id)
})?;
if !response.status().is_success() {
bail!("Failed to list jobs for pipeline: {}", response.status());View on GitHub (pinned to caf1f223d3)
Solutions
- Check the pipeline in GitLab's UI: if it genuinely has >2500 jobs, reduce the job count (matrix fan-out, needs:triggers) or raise MAX_PIPELINE_JOB_PAGES locally with a deliberate decision
- If job count is modest, capture the raw response headers of one jobs request — a repeated X-NextPage/Link header points at a proxy or middleware rewriting them; fix or bypass the proxy for /api/v4
- Retry once: transient header inconsistency under load usually clears
- File the pipeline id and header trace in a but-gitlab issue if GitLab itself cycles pages
Example fix
// before (client-side, nothing to change in the caller's request)
let jobs = client.list_pipeline_jobs_for_ref(project_id, branch).await?;
// after: detect the safety-bail and degrade instead of failing the whole CI view
let jobs = match client.list_pipeline_jobs_for_ref(project_id, branch).await {
Ok(jobs) => jobs,
Err(err) if err.to_string().contains("unsafe pagination state") => {
tracing::warn!(%branch, "pipeline jobs truncated; showing no CI state");
Vec::new()
}
Err(err) => return Err(err),
}; Defensive patterns
Strategy: try-catch
Try / catch
match client.list_pipeline_jobs_for_ref(project_id, branch).await {
Ok(jobs) => jobs,
Err(err) if err.to_string().contains("unsafe pagination state") => {
tracing::error!(%branch, "pipeline jobs listing hit the pagination guard (see pipeline in web UI)");
Vec::new()
}
Err(err) => return Err(err),
} Prevention
- Keep pipelines under ~2500 jobs (25 pages x 100) — cap matrix fan-out
- Ensure proxies/CDNs in front of self-hosted GitLab pass X-NextPage/Link headers through untouched
When it happens
Trigger: A pipeline with more than 2500 jobs (huge matrix builds, tool-generated jobs) exhausts the 25-page cap; a caching proxy or middleware rewrites/drops pagination headers so X-NextPage keeps naming a page already fetched; GitLab returns inconsistent next-page headers under load.
Common situations: Generated matrix pipelines (thousands of jobs) in large repos; self-hosted GitLab behind nginx/CDN that mangles Link/X-NextPage headers; API smoke tests against mocked responses that always return the same next page.
Related errors
- Failed to list jobs for pipeline: {}
- Failed to enable merge request auto-merge: {status} - {error
- Failed to get latest pipeline for ref: {status}
- Stopped listing GitLab merge requests after unsafe paginatio
- Failed to get merge request: {}
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/7321b04bd13a406a.
Report an issue: GitHub.