{"record":{"id":"70cd431c938a7995","repo":"gitbutlerapp/gitbutler","slug":"failed-to-merge-pull-request-status-error-te","errorCode":null,"errorMessage":"Failed to merge pull request: {status} - {error_text}","messagePattern":"Failed to merge pull request: (.+?) - (.+?)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-bitbucket/src/client.rs","lineNumber":435,"sourceCode":"        Ok(())\n    }\n\n    pub async fn merge_pull_request(&self, params: &MergePullRequestParams<'_>) -> Result<()> {\n        let body = MergePullRequestBody {\n            merge_strategy: params.strategy.as_str(),\n        };\n        let url = format!(\n            \"{}/repositories/{}/{}/pullrequests/{}/merge\",\n            self.base_url,\n            urlencoding::encode(params.workspace),\n            urlencoding::encode(params.repo_slug),\n            params.id,\n        );\n        let response = self.client.post(&url).json(&body).send().await?;\n        if !response.status().is_success() {\n            let status = response.status();\n            let error_text = response.text().await.unwrap_or_default();\n            bail!(\"Failed to merge pull request: {status} - {error_text}\");\n        }\n        Ok(())\n    }\n\n    /// Decline (close) a pull request.\n    pub async fn decline_pull_request(\n        &self,\n        workspace: &str,\n        repo_slug: &str,\n        id: i64,\n    ) -> Result<()> {\n        let url = format!(\n            \"{}/repositories/{}/{}/pullrequests/{}/decline\",\n            self.base_url,\n            urlencoding::encode(workspace),\n            urlencoding::encode(repo_slug),\n            id,\n        );","sourceCodeStart":417,"sourceCodeEnd":453,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-bitbucket/src/client.rs#L417-L453","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// pre-check the merge gates you can observe\nlet pr = client.get_pull_request(ws, slug, id).await?;\nanyhow::ensure!(pr.state == \"OPEN\", \"PR not open\");\nif let Some(status) = client.latest_build_status(ws, slug, &pr.source_commit()).await? {\n    anyhow::ensure!(status.is_success(), \"build not green: {}\", status.state);\n}","typeGuard":null,"tryCatchPattern":"match client.merge_pull_request(&params).await {\n    Err(e) if e.to_string().contains(\"409\") => {\n        // someone else may have merged it — refresh and treat merged as done\n        let pr = client.get_pull_request(params.workspace, params.repo_slug, params.id).await?;\n        if pr.state == \"MERGED\" { Ok(()) } else { Err(e) }\n    }\n    r => r,\n}","preventionTips":["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"],"tags":["bitbucket","pull-request","merge","http"],"backgroundTag":"pull-request-merge-failed","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}