{"record":{"id":"cedce728b22a5fa3","repo":"gitbutlerapp/gitbutler","slug":"failed-to-withdraw-review-request","errorCode":null,"errorMessage":"Failed to withdraw review request: {}","messagePattern":"Failed to withdraw review request: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-github/src/client.rs","lineNumber":915,"sourceCode":"        owner: &str,\n        repo: &str,\n        pr_number: i64,\n        reviewers: &[String],\n    ) -> Result<()> {\n        let url = format!(\n            \"{}/repos/{}/{}/pulls/{}/requested_reviewers\",\n            self.base_url, owner, repo, pr_number\n        );\n\n        let response = self\n            .client\n            .delete(&url)\n            .json(&ReviewersBody { reviewers })\n            .send()\n            .await?;\n\n        if !response.status().is_success() {\n            bail!(\n                \"Failed to withdraw review request: {}\",\n                response_error(response).await\n            );\n        }\n\n        Ok(())\n    }\n\n    /// Edit a conversation comment. Comments are addressed by their own id,\n    /// not the pull request number.\n    pub async fn update_pull_request_comment(\n        &self,\n        owner: &str,\n        repo: &str,\n        comment_id: i64,\n        body: &str,\n    ) -> Result<PullRequestComment> {\n        #[derive(Serialize)]","sourceCodeStart":897,"sourceCodeEnd":933,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-github/src/client.rs#L897-L933","documentation":"This error comes from `GithubClient::remove_requested_reviewers`, which DELETEs `/repos/{owner}/{repo}/pulls/{n}/requested_reviewers` to withdraw a review request. Any non-success HTTP status is converted to this error via `response_error`. It means the review request was not withdrawn.","triggerScenarios":"A `remove_requested_reviewers(owner, repo, pr_number, reviewers)` call where GitHub answers non-2xx — 404 (the reviewer is not currently requested, or wrong repo/PR), 401/403 (insufficient token permissions), 422 (malformed login list), or 403 rate limiting.","commonSituations":"Trying to withdraw a request that was already accepted or declined (so it is no longer 'pending'); the login was removed as a collaborator meanwhile; token lacks write access; typo in the reviewer login; UI state was stale because someone else withdrew the request first.","solutions":["Inspect the status/body embedded in the error message (404 vs 403 vs 422) to pick the fix.","Treat 404 as 'nothing to withdraw': re-fetch the PR's requested reviewers before deleting.","Grant the token pull-request write permission and re-run.","Check the reviewer login spelling against `GET /pulls/{n}/requested_reviewers`.","Retry with backoff if the status is 403 and rate-limit headers show exhaustion."],"exampleFix":"// before\nclient.remove_requested_reviewers(owner, repo, pr, &[login]).await?; // 404 if not pending\n// after\nlet pending = client.list_requested_reviewers(owner, repo, pr).await?;\nif pending.iter().any(|r| r.login == login) {\n    client.remove_requested_reviewers(owner, repo, pr, &[login]).await?;\n}","handlingStrategy":"try-catch","validationCode":"let pending = client.list_requested_reviewers(owner, repo, pr).await?;\nlet to_remove: Vec<_> = reviewers.iter().filter(|r| pending.iter().any(|p| &p.login == *r)).collect();\nif to_remove.is_empty() { return Ok(()); } // nothing pending: skip DELETE","typeGuard":"fn is_pending(pending: &[Reviewer], login: &str) -> bool { pending.iter().any(|p| p.login == login) }","tryCatchPattern":"match client.remove_requested_reviewers(owner, repo, pr, &reviewers).await {\n    Ok(()) => {},\n    Err(e) if e.to_string().contains(\"404\") => {}, // not requested anymore: idempotent ok\n    Err(e) => return Err(e),\n}","preventionTips":["Re-fetch pending reviewers before withdrawing to avoid stale UI state.","Treat 404 as already-withdrawn and continue.","Keep tokens scoped with pull-request write permission.","Validate reviewer logins against the collaborator list."],"tags":["github-api","http-error-response","rest","network"],"backgroundTag":"http-error-response","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"}