{"record":{"id":"08bdcfe85bfeee6b","repo":"gitbutlerapp/gitbutler","slug":"error-message","errorCode":null,"errorMessage":"{error_message}: {}","messagePattern":"\\{error_message\\}: \\{\\}","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-gitlab/src/client.rs","lineNumber":209,"sourceCode":"    ) -> Result<Vec<MergeRequest>> {\n        let mut mrs = Vec::new();\n        let mut next_page = Some(\"1\".to_string());\n        let mut seen_pages = HashSet::new();\n\n        while let Some(page) = next_page.take() {\n            if !merge_request_page_is_safe(&page, &mut seen_pages) {\n                bail!(\"Stopped listing GitLab merge requests after unsafe pagination state\");\n            }\n\n            let response = self\n                .client\n                .get(url)\n                .query(query)\n                .query(&[(\"per_page\", \"100\"), (\"page\", page.as_str())])\n                .send()\n                .await?;\n            if !response.status().is_success() {\n                bail!(\"{error_message}: {}\", response.status());\n            }\n\n            next_page = next_page_from_headers(response.headers());\n            let mut page_mrs: Vec<GitLabMergeRequest> = response.json().await?;\n            if page_mrs.is_empty() {\n                break;\n            }\n            mrs.append(&mut page_mrs);\n        }\n\n        let mut seen = HashSet::new();\n        Ok(mrs\n            .into_iter()\n            .filter(|mr| seen.insert(mr.iid))\n            .map(Into::into)\n            .collect())\n    }\n","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-gitlab/src/client.rs#L191-L227","documentation":"A GET request while listing GitLab merge requests returned a non-2xx status. The message prefixes the caller-supplied context (which listing failed) and appends the HTTP status code.","triggerScenarios":"401 expired GitLab token; 403 token lacks the api or read_api scope; 404 wrong project path or id in the URL; 429 rate limit; 5xx under GitLab load.","commonSituations":"Long-lived sessions with expired tokens; wrong project identifiers; heavy automation against self-hosted GitLab.","solutions":["401: re-authenticate the GitLab token","403: add the api (or read_api) scope to the token","404: fix the project path/id used in the URL","429/5xx: retry with backoff"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"for attempt in 0..3 {\n    match client.list_merge_requests(...).await {\n        Ok(mrs) => break Ok(mrs),\n        Err(e) if e.to_string().ends_with(\"401 Unauthorized\") => break Err(e.context(\"re-authenticate GitLab token\")),\n        Err(e) if e.to_string().ends_with(\"404 Not Found\") => break Err(e.context(\"check project path/id\")),\n        Err(e) => tokio::time::sleep(exp_backoff(attempt)).await,\n    }\n}","preventionTips":["Give tokens the api or read_api scope up front","Verify the project path/id before listing","Retry 429/5xx with backoff, but never retry 401/404"],"tags":["gitlab","http","merge-requests","status-code"],"backgroundTag":"gitlab-api-request-failed","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}