{"record":{"id":"727a1379a22f2eb1","repo":"gitbutlerapp/gitbutler","slug":"bitbucket-pagination-exceeded-the-max-pages-page","errorCode":null,"errorMessage":"Bitbucket pagination exceeded the {MAX_PAGES}-page safety cap","messagePattern":"Bitbucket pagination exceeded the (.+?)-page safety cap","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-bitbucket/src/client.rs","lineNumber":100,"sourceCode":"        Ok(AuthenticatedUser {\n            username: user.username(),\n            avatar_url: user.avatar_url(),\n            name: user.display_name,\n            account_id: user.account_id,\n        })\n    }\n\n    /// Fetch every `values` entry across a paginated Bitbucket collection,\n    /// following the `next` cursor URL until exhausted. Errors out if the\n    /// `MAX_PAGES` safety cap is hit rather than silently truncating the result.\n    async fn get_paginated<T: DeserializeOwned>(&self, initial_url: String) -> Result<Vec<T>> {\n        let mut items = Vec::new();\n        let mut next = Some(initial_url);\n        let mut pages = 0;\n\n        while let Some(url) = next.take() {\n            if pages >= MAX_PAGES {\n                bail!(\"Bitbucket pagination exceeded the {MAX_PAGES}-page safety cap\");\n            }\n            pages += 1;\n\n            let response = self.client.get(&url).send().await?;\n            if !response.status().is_success() {\n                bail!(\"Bitbucket request failed: {}\", response.status());\n            }\n            let page: Paginated<T> = response.json().await?;\n            items.extend(page.values);\n            next = page.next;\n        }\n\n        Ok(items)\n    }\n\n    /// Fetch a single page of a Bitbucket collection without following the\n    /// `next` cursor. Used for \"most recent\" listings where the caller sorts\n    /// server-side and only needs the head of the result set.","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-bitbucket/src/client.rs#L82-L118","documentation":"get_paginated() in but-bitbucket follows the 'next' cursor of a Bitbucket collection and hard-caps the walk at MAX_PAGES (25, defined at crates/but-bitbucket/src/client.rs:11). Hitting the cap raises this error instead of silently returning a truncated list, so callers never mistake a partial result for the full set.","triggerScenarios":"Calling a Bitbucket listing that uses get_paginated (PRs, build statuses, etc.) on a workspace/repo whose collection spans more than 25 pages — e.g. more than ~1250 open pull requests at pagelen=50 — or when a misbehaving server keeps returning a 'next' URL that never drains.","commonSituations":"Large monorepos or org accounts with thousands of PRs/build statuses; CI noise generating many build-status pages; a stale 'next' cursor after API changes.","solutions":["Narrow the server-side filter (state=OPEN, narrower repo/workspace scope, date filters) so the result set fits in 25 pages","If you only need the newest entries, use the single-page path (get_first_page) instead of full pagination","If you genuinely must fetch more, raise the MAX_PAGES constant in crates/but-bitbucket/src/client.rs and rebuild, accepting the longer run time","Log the last visited URL to check whether the 'next' cursor is looping instead of advancing (server-side pagination bug)"],"exampleFix":"// before: every open PR, can exceed the 25-page cap on huge repos\nlet prs = client.list_open_prs(ws, slug).await?;\n\n// after: only the most recent page is needed for 'latest PR' views\nlet prs = client.list_recent_prs_first_page(ws, slug).await?; // get_first_page-based","handlingStrategy":"fallback","validationCode":"// estimate up front: you cannot cheaply count pages, but you can narrow the query\nlet url = format!(\"{base}/repositories/{ws}/{slug}/pullrequests?state=OPEN&pagelen=50&q=updated_on>={week_ago}\");","typeGuard":null,"tryCatchPattern":"// degrade gracefully instead of failing the whole feature\nmatch client.list_all_build_statuses(ws, slug, sha).await {\n    Err(e) if e.to_string().contains(\"safety cap\") => client.list_first_page_statuses(ws, slug, sha).await,\n    r => r,\n}","preventionTips":["Filter listings server-side (state, date, author) to keep result sets small","Prefer get_first_page-style helpers for 'most recent' views","Alert on the cap error: it also detects looping 'next' cursors"],"tags":["bitbucket","pagination","api-limits","forge"],"backgroundTag":"api-pagination-limit","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}