{"record":{"id":"f30d2882add265cf","repo":"gitbutlerapp/gitbutler","slug":"github-refused-the-merge-status-reason","errorCode":null,"errorMessage":"GitHub refused the merge ({status}): {reason}","messagePattern":"GitHub refused the merge \\((.+?)\\): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-github/src/client.rs","lineNumber":1492,"sourceCode":"            merge_method,\n        };\n\n        let response = self.client.put(&url).json(&body).send().await?;\n\n        let status = response.status();\n        if !status.is_success() {\n            // The body carries GitHub's reason. Only its `message` reads well in\n            // a toast; the raw envelope drags a documentation URL along.\n            let body = response.text().await.unwrap_or_default();\n            let reason = serde_json::from_str::<serde_json::Value>(&body)\n                .ok()\n                .and_then(|value| {\n                    value\n                        .get(\"message\")\n                        .and_then(|m| m.as_str().map(String::from))\n                })\n                .unwrap_or(body);\n            bail!(\"GitHub refused the merge ({status}): {reason}\");\n        }\n\n        Ok(())\n    }\n\n    /// Set the draftiness of a PR.\n    pub async fn set_pull_request_draft_state(\n        &self,\n        params: &SetPullRequestDraftStateParams<'_>,\n    ) -> Result<()> {\n        if params.pr_number <= 0 {\n            bail!(\"PR number must be greater than 0\");\n        }\n\n        let pull_request_id = self\n            .get_pull_request_node_id(params.owner, params.repo, params.pr_number)\n            .await?;\n","sourceCodeStart":1474,"sourceCodeEnd":1510,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-github/src/client.rs#L1474-L1510","documentation":"The pull request merge REST call completed at the transport level but GitHub returned an error status; the library extracts the 'message' field from the JSON error body (falling back to the raw body) and bails with the status code and reason. GitHub refuses merges for many precondition failures that are not network errors.","triggerScenarios":"Calling the merge PR API when: the merge method is not allowed on the repo, the branch has conflicting/unmergeable state, required status checks or reviews are missing, branch protection blocks the merge, or the PR was already merged/closed.","commonSituations":"CI still running so required checks are pending; merge queue enabled so direct merge is disallowed; 'Allow merge commit' disabled in repo settings while requesting merge method 'merge'; another bot merged the PR first (422 already merged).","solutions":["Parse the status/reason in the message: 405 usually means the merge method is disallowed or conflicts exist, 409 means no mergeable state, 422 often means already merged","Wait for required status checks/reviews to pass before merging","Use a merge method enabled in the repo settings, or enable it in repo settings","If a merge queue is enabled, add the PR to the queue instead of calling the direct merge endpoint"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// precheck mergeability and required checks before merging\nlet pr = client.get_pull_request(owner, repo, pr_number).await?;\nif !pr.mergeable || pr.draft { return Err(anyhow!(\"PR not mergeable yet\")); }","typeGuard":null,"tryCatchPattern":"// branch on the status embedded in the message\nmatch client.merge_pull_request(&params).await {\n    Err(e) if e.to_string().contains(\"405\") => eprintln!(\"conflict or merge method disallowed\"),\n    Err(e) if e.to_string().contains(\"409\") => eprintln!(\"not mergeable yet; wait for checks\"),\n    Err(e) => return Err(e),\n    Ok(()) => {},\n}","preventionTips":["Wait for required status checks and reviews before merging","Only request merge methods enabled in repo settings","Check mergeable state and skip the call when the PR is already merged","Use the merge queue API when branch protection requires it"],"tags":["github","merge","pull-request","branch-protection"],"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"}