gitbutlerapp/gitbutler · error

Failed to withdraw review request

Error message

Failed to withdraw review request: {}

What it means

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.

Solutions

  1. Inspect the status/body embedded in the error message (404 vs 403 vs 422) to pick the fix.
  2. Treat 404 as 'nothing to withdraw': re-fetch the PR's requested reviewers before deleting.
  3. Grant the token pull-request write permission and re-run.
  4. Check the reviewer login spelling against `GET /pulls/{n}/requested_reviewers`.
  5. Retry with backoff if the status is 403 and rate-limit headers show exhaustion.

Example fix

// before
client.remove_requested_reviewers(owner, repo, pr, &[login]).await?; // 404 if not pending
// after
let pending = client.list_requested_reviewers(owner, repo, pr).await?;
if pending.iter().any(|r| r.login == login) {
    client.remove_requested_reviewers(owner, repo, pr, &[login]).await?;
}
Defensive patterns

Strategy: try-catch

Validate before calling

let pending = client.list_requested_reviewers(owner, repo, pr).await?;
let to_remove: Vec<_> = reviewers.iter().filter(|r| pending.iter().any(|p| &p.login == *r)).collect();
if to_remove.is_empty() { return Ok(()); } // nothing pending: skip DELETE

Type guard

fn is_pending(pending: &[Reviewer], login: &str) -> bool { pending.iter().any(|p| p.login == login) }

Try / catch

match client.remove_requested_reviewers(owner, repo, pr, &reviewers).await {
    Ok(()) => {},
    Err(e) if e.to_string().contains("404") => {}, // not requested anymore: idempotent ok
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/cedce728b22a5fa3. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-github/src/client.rs:915

        owner: &str,
        repo: &str,
        pr_number: i64,
        reviewers: &[String],
    ) -> Result<()> {
        let url = format!(
            "{}/repos/{}/{}/pulls/{}/requested_reviewers",
            self.base_url, owner, repo, pr_number
        );

        let response = self
            .client
            .delete(&url)
            .json(&ReviewersBody { reviewers })
            .send()
            .await?;

        if !response.status().is_success() {
            bail!(
                "Failed to withdraw review request: {}",
                response_error(response).await
            );
        }

        Ok(())
    }

    /// Edit a conversation comment. Comments are addressed by their own id,
    /// not the pull request number.
    pub async fn update_pull_request_comment(
        &self,
        owner: &str,
        repo: &str,
        comment_id: i64,
        body: &str,
    ) -> Result<PullRequestComment> {
        #[derive(Serialize)]

View on GitHub (pinned to 58e5313667)