{"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":null,"httpStatus":null,"severity":"error","filePath":"crates/but-bitbucket/src/client.rs","lineNumber":101,"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":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-bitbucket/src/client.rs#L83-L119","documentation":"Bitbucket list requests that follow the `next` cursor are capped at MAX_PAGES to prevent unbounded pagination loops. When get_paginated has already fetched MAX_PAGES pages and another `next` link exists, it bails instead of fetching more. This guards against pathological or broken server-side pagination.","triggerScenarios":"Calling list_open_prs or list_checks_for_ref when Bitbucket returns more than MAX_PAGES pages of results, or when the `next` link never terminates (API bug/misbuilt URL).","commonSituations":"Large repositories with hundreds of open PRs; build-status queries for a ref with a long CI history; a misbehaving Bitbucket API returning self-referencing next links.","solutions":["Narrow the query so fewer pages are returned (filter by state, target branch, or date)","Raise the MAX_PAGES constant in crates/but-bitbucket/src/client.rs if the legitimate result set is genuinely large","Handle the returned error by paging manually or caching results instead of listing everything at once"],"exampleFix":"// before\nlet prs = client.list_open_prs(workspace, repo_slug).await?;\n// after\nlet prs = match client.list_open_prs(workspace, repo_slug).await {\n    Ok(prs) => prs,\n    Err(e) if e.to_string().contains(\"safety cap\") => vec![],\n    Err(e) => return Err(e),\n};","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match client.list_open_prs(ws, slug).await {\n    Err(e) if e.to_string().contains(\"safety cap\") => vec![], // degrade gracefully\n    other => other?,\n}","preventionTips":["Filter listings server-side (state, branch, date) to keep result sets small","Avoid unbounded full-listing calls in hot paths; cache results","If large listings are expected, raise MAX_PAGES consciously and monitor request volume"],"tags":["network","pagination","bitbucket","api"],"backgroundTag":"http-request-failed","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}