{"record":{"id":"481be2515832522d","repo":"gitbutlerapp/gitbutler","slug":"open-pull-request-listing-exceeded-max-pages-pages","errorCode":null,"errorMessage":"Open pull request listing exceeded {MAX_PAGES} pages","messagePattern":"Open pull request listing exceeded (.+?) pages","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-github/src/client.rs","lineNumber":302,"sourceCode":"    /// Pages are fetched in ascending creation-time order so offset\n    /// pagination stays stable while the repository is active: updates never\n    /// move a PR and new PRs only append. A close or reopen racing the scan\n    /// can still shift a page boundary, duplicating a boundary PR (the\n    /// freshest copy is kept) or omitting one (a later refresh restores it,\n    /// though the review cache may briefly drop its row in between).\n    pub async fn list_open_pulls(&self, owner: &str, repo: &str) -> Result<Vec<PullRequest>> {\n        const MAX_PAGES: usize = 100;\n        let url = format!(\n            \"{}/repos/{}/{}/pulls?state=open&sort=created&direction=asc\",\n            self.base_url, owner, repo\n        );\n        let pulls = self\n            .get_all_pages::<GitHubPullRequest>(&url, MAX_PAGES + 1)\n            .await?;\n        // Every fetched page being full means the listing may extend past\n        // what was fetched; a short final page is complete however far past\n        // the nominal bound it runs.\n        anyhow::ensure!(\n            pulls.len() < (MAX_PAGES + 1) * 100,\n            \"Open pull request listing exceeded {MAX_PAGES} pages\"\n        );\n        // Later duplicates overwrite earlier ones, keeping the freshest copy.\n        let pulls: std::collections::BTreeMap<_, _> =\n            pulls.into_iter().map(|pull| (pull.number, pull)).collect();\n        let mut pulls = pulls.into_values().collect::<Vec<_>>();\n        // RFC 3339 timestamps compare chronologically as strings; consumers\n        // that pick one PR per branch rely on the freshest coming first.\n        pulls.sort_by(|a, b| b.updated_at.cmp(&a.updated_at));\n        Ok(pulls.into_iter().map(Into::into).collect())\n    }\n\n    /// List the PRs for a given target.\n    pub async fn list_pulls_for_base(\n        &self,\n        owner: &str,\n        repo: &str,","sourceCodeStart":284,"sourceCodeEnd":320,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-github/src/client.rs#L284-L320","documentation":"`list_open_pulls` pages through GitHub's PR listing with `get_all_pages` fetching up to `MAX_PAGES + 1` pages of 100 items; `anyhow::ensure!` fails with this message when the accumulated result reaches the full `(MAX_PAGES + 1) * 100` size, meaning more pages likely exist than the hard safety bound allows. It prevents unbounded memory growth and runaway pagination on very large repositories.","triggerScenarios":"Listing open PRs on a repository with more than `MAX_PAGES * 100` open pull requests (each page holding 100 items).","commonSituations":"Very large monorepos or bots creating thousands of open PRs; a CI fleet that never closes PRs; repositories with thousands of stale automated dependency-update PRs.","solutions":["Reduce the number of open PRs (close or merge stale ones, e.g. automated dependency PRs).","Narrow the query to a subset of PRs if the API supports filters (branch, label) so pagination stays within bounds.","Increase `MAX_PAGES` if you maintain the code and genuinely need larger listings.","Use a targeted PR fetch by number instead of listing all open PRs."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// Before listing, cap work: skip listing when repo is known to exceed the bound\n// (e.g. track open-PR count from a prior run and refuse if >= MAX_PAGES * 100).","typeGuard":null,"tryCatchPattern":"match client.list_open_pulls(owner, repo).await {\n    Ok(pulls) => pulls,\n    Err(e) if e.to_string().contains(\"exceeded\") => {\n        eprintln!(\"Too many open PRs for a full listing; use a targeted fetch by number\");\n        Vec::new()\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Close/merge stale and automated PRs regularly to keep listings small","Prefer fetching PRs by number over full open-PR listings","Use branch/label filters to narrow listings where available"],"tags":["github","pagination","limit","pull-request"],"backgroundTag":"value-out-of-range","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"}