gitbutlerapp/gitbutler · error · anyhow::Error
Failed to list jobs for pipeline: {}
Error message
Failed to list jobs for pipeline: {} What it means
Thrown inside list_pipeline_jobs_for_ref when one page request of GET /projects/:id/pipelines/:id/jobs (per_page=100&page=N) returns a non-2xx status after the initial pipeline lookup already succeeded. Distinct from the context-wrapped transport error ('Failed to list GitLab jobs for pipeline {id}') which fires if the request never completes — this bail means GitLab answered and refused. Because it happens mid-pagination, all jobs accumulated so far are discarded.
Source
Thrown at crates/but-gitlab/src/client.rs:690
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());
}
next_page = next_page_from_headers(response.headers());
let mut page_jobs: Vec<GitLabPipelineJob> =
response.json().await.with_context(|| {
format!("Failed to parse GitLab jobs for pipeline {}", pipeline.id)
})?;
if page_jobs.is_empty() {
break;
}
jobs.append(&mut page_jobs);
}
let jobs = normalize_pipeline_jobs(
jobs,
pipeline_web_url,
pipeline_status,
&self.base_url,View on GitHub (pinned to caf1f223d3)
Solutions
- For 429: back off (honor Retry-After) and restart the listing — this client does not cache partial pages
- For 401: re-run `but config forge auth` and retry
- For 5xx: retry with exponential backoff; check GitLab status
- Reduce polling frequency of large pipelines to stay under rate limits
Example fix
// before
let jobs = client.list_pipeline_jobs_for_ref(project_id, branch).await?;
// after: retry once after backoff for transient mid-pagination failures
let jobs = match client.list_pipeline_jobs_for_ref(project_id.clone(), branch).await {
Ok(jobs) => jobs,
Err(err) => {
let s = err.to_string();
if s.contains("429") || s.contains("500") || s.contains("502") || s.contains("503") {
tokio::time::sleep(Duration::from_secs(5)).await;
client.list_pipeline_jobs_for_ref(project_id, branch).await?
} else {
return Err(err);
}
}
}; Defensive patterns
Strategy: retry
Try / catch
async fn list_jobs_with_retry(client: &GitLabClient, project_id: GitLabProjectId, branch: &str) -> Result<Vec<GitLabPipelineJob>> {
match client.list_pipeline_jobs_for_ref(project_id.clone(), branch).await {
Ok(jobs) => Ok(jobs),
Err(err) => {
let m = err.to_string();
if m.contains("429") || m.contains("50") { // transient: retry once after backoff
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
client.list_pipeline_jobs_for_ref(project_id, branch).await
} else { Err(err) }
}
}
} Prevention
- Rate-limit concurrent pipeline-job listings; large pipelines multiplied by polling easily trip 429
- Retry the whole listing (not per page) — the client discards partial pages on failure
When it happens
Trigger: Page 3+ of the jobs listing returns 429 (rate limit) after pages 1-2 succeeded; token revoked between the pipeline fetch and the jobs fetch (401); a 5xx mid-listing; permissions changed mid-walk so later pages 403.
Common situations: Deep pagination against rate limits when several large pipelines are polled concurrently; long-running token expiry hitting mid-request-sequence; GitLab rolling restarts during incidents.
Related errors
- Stopped listing GitLab jobs for pipeline {} after unsafe pag
- Failed to enable merge request auto-merge: {status} - {error
- Failed to get latest pipeline for ref: {status}
- Failed to get merge request: {}
- Failed to get MR merge status: {}
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/310750cfe42a298e.
Report an issue: GitHub.