gitbutlerapp/gitbutler · error · anyhow::Error

Stopped listing GitLab merge requests after unsafe paginatio

Error message

Stopped listing GitLab merge requests after unsafe pagination state

What it means

list_merge_requests follows GitLab's x-next-page header page by page (per_page=100). Before each request it validates the page number: it must parse as a positive integer, must not have been visited already, and the total number of fetched pages must stay under 10,000 (MAX_MERGE_REQUEST_REQUESTS). When the server's pagination headers violate these rules - typically by replaying the same next page forever - the loop would never terminate, so the client stops and fails instead.

Source

Thrown at crates/but-gitlab/src/client.rs:198

        );

        self.list_merge_requests(&url, &[], "Failed to list merge requests for commit")
            .await
    }

    async fn list_merge_requests(
        &self,
        url: &str,
        query: &[(&str, &str)],
        error_message: &str,
    ) -> Result<Vec<MergeRequest>> {
        let mut mrs = Vec::new();
        let mut next_page = Some("1".to_string());
        let mut seen_pages = HashSet::new();

        while let Some(page) = next_page.take() {
            if !merge_request_page_is_safe(&page, &mut seen_pages) {
                bail!("Stopped listing GitLab merge requests after unsafe pagination state");
            }

            let response = self
                .client
                .get(url)
                .query(query)
                .query(&[("per_page", "100"), ("page", page.as_str())])
                .send()
                .await?;
            if !response.status().is_success() {
                bail!("{error_message}: {}", response.status());
            }

            next_page = next_page_from_headers(response.headers());
            let mut page_mrs: Vec<GitLabMergeRequest> = response.json().await?;
            if page_mrs.is_empty() {
                break;
            }

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Retry the operation - header anomalies are often transient
  2. If reproducible, capture the x-next-page headers (curl -I) and check the GitLab version and any proxy configuration
  3. Narrow the listing with filters (state, assignee, updated_after) to reduce the page count
Defensive patterns

Strategy: retry

Try / catch

let mrs = match client.list_merge_requests(...).await {
    Err(e) if e.to_string().contains("unsafe pagination state") => {
        tokio::time::sleep(Duration::from_secs(5)).await;
        client.list_merge_requests(...).await? // one retry: header glitches are usually transient
    }
    other => other?,
};

Prevention

When it happens

Trigger: A GitLab instance or a proxy in front of it returns a duplicate x-next-page value so the same page repeats; a non-numeric or zero next-page header; a listing so vast it would exceed 10,000 pages of 100 merge requests.

Common situations: Self-managed GitLab versions with pagination bugs; reverse proxies that mangle response headers; extremely large projects.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/7088f6762f7d2c71. Report an issue: GitHub.