gitbutlerapp/gitbutler · error
Failed to decline pull request
Error message
Failed to decline pull request: {status} - {error_text} What it means
Thrown by decline_pull_request when the HTTP POST that declines (closes without merging) a Bitbucket pull request returns a non-success status. The message includes the HTTP status and Bitbucket's error body. Most commonly the PR is no longer in a state that can be declined.
Solutions
- Fetch the PR's current state first and skip declining if already merged/declined
- Verify the PR id is correct and the PR is still open
- Check the token has write permission on the repository
- Treat 'already closed' style error bodies as success in idempotent automation
Example fix
// before
client.decline_pull_request(workspace, repo_slug, id).await?;
// after
match client.decline_pull_request(workspace, repo_slug, id).await {
Ok(()) => {},
Err(e) if e.to_string().contains("400") => { /* already merged/declined; ignore */ },
Err(e) => return Err(e),
} Defensive patterns
Strategy: try-catch
Validate before calling
let pr_state = get_pr_state(ws, slug, id)?;
anyhow::ensure!(pr_state == "OPEN", "PR #{id} already closed; skip decline"); Try / catch
match client.decline_pull_request(ws, slug, id).await {
Err(e) if e.to_string().contains("400") || e.to_string().contains("409") => { /* already merged/declined */ },
other => other?,
} Prevention
- Re-fetch PR state before declining in retries
- Treat decline as idempotent in automation
- Use write-scoped tokens
- Avoid stale cached PR ids
When it happens
Trigger: Declining a PR that was already merged or already declined (400/409), wrong PR id (404), or a token lacking write access (403/401).
Common situations: Race with another teammate who merged/closed the PR first; stale cached PR id; automation retrying decline on an already-closed PR; bot token with read-only scope.
Related errors
- Failed to merge pull request
- Failed to update pull request
- Bitbucket request failed
- Bitbucket request failed
- Failed to create pull request
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/f35955e1e819b23c.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-bitbucket/src/client.rs:484
/// Decline (close) a pull request.
pub async fn decline_pull_request(
&self,
workspace: &str,
repo_slug: &str,
id: i64,
) -> Result<()> {
let url = format!(
"{}/repositories/{}/{}/pullrequests/{}/decline",
self.base_url,
urlencoding::encode(workspace),
urlencoding::encode(repo_slug),
id,
);
let response = self.client.post(&url).send().await?;
if !response.status().is_success() {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
bail!("Failed to decline pull request: {status} - {error_text}");
}
Ok(())
}
pub async fn fetch_repo(&self, workspace: &str, repo_slug: &str) -> Result<BitbucketRepo> {
let url = format!(
"{}/repositories/{}/{}",
self.base_url,
urlencoding::encode(workspace),
urlencoding::encode(repo_slug),
);
let response = self.client.get(&url).send().await?;
if !response.status().is_success() {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
bail!("Failed to fetch repository: {status} - {error_text}");
}
let repo: BitbucketApiRepository = response.json().await?;View on GitHub (pinned to 58e5313667)