gitbutlerapp/gitbutler · error · anyhow::Error
Failed to merge pull request: {status} - {error_text}
Error message
Failed to merge pull request: {status} - {error_text} What it means
Raised when POSTing to /repositories/{workspace}/{repo_slug}/pullrequests/{id}/merge returns non-2xx; the message includes the status and Bitbucket's response body, which names the unmet merge precondition. The request body is built just above (merge strategy/close-source flags), so invalid strategy combinations also surface here.
Source
Thrown at crates/but-bitbucket/src/client.rs:435
Ok(())
}
pub async fn merge_pull_request(&self, params: &MergePullRequestParams<'_>) -> Result<()> {
let body = MergePullRequestBody {
merge_strategy: params.strategy.as_str(),
};
let url = format!(
"{}/repositories/{}/{}/pullrequests/{}/merge",
self.base_url,
urlencoding::encode(params.workspace),
urlencoding::encode(params.repo_slug),
params.id,
);
let response = self.client.post(&url).json(&body).send().await?;
if !response.status().is_success() {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
bail!("Failed to merge pull request: {status} - {error_text}");
}
Ok(())
}
/// 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,
);View on GitHub (pinned to caf1f223d3)
Solutions
- Parse {error_text} — Bitbucket states the failed merge check (conflicts, approvals, builds)
- Re-fetch the PR and verify state is OPEN and all checks green before retrying
- Resolve conflicts / collect approvals, then retry the merge
- Treat 'already merged' 409s as success by refreshing PR state instead of surfacing an error
Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check the merge gates you can observe
let pr = client.get_pull_request(ws, slug, id).await?;
anyhow::ensure!(pr.state == "OPEN", "PR not open");
if let Some(status) = client.latest_build_status(ws, slug, &pr.source_commit()).await? {
anyhow::ensure!(status.is_success(), "build not green: {}", status.state);
} Try / catch
match client.merge_pull_request(¶ms).await {
Err(e) if e.to_string().contains("409") => {
// someone else may have merged it — refresh and treat merged as done
let pr = client.get_pull_request(params.workspace, params.repo_slug, params.id).await?;
if pr.state == "MERGED" { Ok(()) } else { Err(e) }
}
r => r,
} Prevention
- Poll merge readiness (state, builds, approvals) rather than blind-firing the merge
- Serialize merges per PR id so two clients cannot race
- Surface error_text verbatim; Bitbucket names the failed merge check
When it happens
Trigger: Merging a PR with unresolved conflicts, failing or missing required builds, missing required approvals (branch restrictions), a PR already merged or declined (409), or a merge strategy the repo does not permit.
Common situations: Automation merging as soon as CI finishes while required reviewers are still pending; double-merge races between two clients; repo admins tightening branch restrictions after the PR was opened.
Related errors
- Failed to update pull request: {status} - {error_text}
- Failed to decline pull request: {status} - {error_text}
- Failed to create pull request: {status} - {error_text}
- Bitbucket request failed: {}
- Failed to fetch repository: {status} - {error_text}
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/70cd431c938a7995.
Report an issue: GitHub.