{"record":{"id":"6130f2f1b1cdfe36","repo":"gitbutlerapp/gitbutler","slug":"failed-to-disable-merge-request-auto-merge-statu","errorCode":null,"errorMessage":"Failed to disable merge request auto-merge: {status} - {error_text}","messagePattern":"Failed to disable merge request auto-merge: (.+?) - (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-gitlab/src/client.rs","lineNumber":516,"sourceCode":"                let status = response.status();\n                let error_text = response.text().await.unwrap_or_default();\n                bail!(\"Failed to enable merge request auto-merge: {status} - {error_text}\");\n            }\n\n            return Ok(());\n        }\n\n        let url = format!(\n            \"{}/projects/{}/merge_requests/{}/cancel_merge_when_pipeline_succeeds\",\n            self.base_url, project_id, params.mr_iid\n        );\n\n        let response = self.client.post(&url).send().await?;\n\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 disable merge request auto-merge: {status} - {error_text}\");\n        }\n\n        Ok(())\n    }\n\n    pub async fn fetch_project(&self, project_id: GitLabProjectId) -> Result<GitLabProject> {\n        self.fetch_project_by_path(project_id.to_string()).await\n    }\n\n    pub async fn fetch_project_by_numeric_id(&self, project_id: i64) -> Result<GitLabProject> {\n        self.fetch_project_by_path(project_id.to_string()).await\n    }\n\n    async fn fetch_project_by_path(&self, project_id: String) -> Result<GitLabProject> {\n        #[derive(Deserialize)]\n        struct GitLabApiProject {\n            id: i64,\n            path_with_namespace: String,","sourceCodeStart":498,"sourceCodeEnd":534,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-gitlab/src/client.rs#L498-L534","documentation":"Thrown by GitLabClient::set_merge_request_auto_merge when params.enabled == false and GitLab rejects POST /projects/:id/merge_requests/:iid/cancel_merge_when_pipeline_succeeds. The dominant real-world failure is 406 Not Acceptable, which GitLab returns when auto-merge was never enabled — you cannot cancel what is not pending. Status and body are included in the message.","triggerScenarios":"Cancelling auto-merge on an MR where it was never enabled (406); auto-merge already fired because the pipeline finished and the MR merged (406/404); MR deleted while the cancel request was in flight (404); no permission (403).","commonSituations":"UI shows a cancel button based on stale state after the pipeline finished and auto-merge already executed; user cancelled in the web UI and the desktop client retries; double-click on cancel.","solutions":["Treat 406 on cancel as success (the end state — auto-merge off — already holds)","Refresh MR state before showing the cancel affordance, and hide it when merge_status is not pending auto-merge","For 404, drop the stale MR from local state"],"exampleFix":"// before\nclient.set_merge_request_auto_merge(&params).await?;\n\n// after: idempotent cancel\nif let Err(err) = client.set_merge_request_auto_merge(&params).await {\n    let msg = err.to_string();\n    if !(msg.contains(\"406\") || msg.contains(\"404\")) {\n        return Err(err); // 406 = never enabled, 404 = MR gone; both mean 'nothing to cancel'\n    }\n}","handlingStrategy":"fallback","validationCode":"// Only offer cancel when MR state actually shows pending auto-merge (from a fresh get_merge_request)\nensure!(mr.merge_status_is_auto_merge_pending(), \"auto-merge not enabled for MR {}\"); // conceptually\nclient.set_merge_request_auto_merge(&params).await?;","typeGuard":null,"tryCatchPattern":"if let Err(err) = client.set_merge_request_auto_merge(&params).await {\n    let m = err.to_string();\n    if m.contains(\"406\") || m.contains(\"404\") { /* nothing to cancel — treat as success */ }\n    else { return Err(err); }\n}","preventionTips":["Make cancel idempotent in the UI: hide the button the moment auto-merge state is confirmed off","Refresh MR state before showing cancel, since pipelines finishing can flip state underneath you"],"tags":["gitlab","http","auto-merge","idempotency","rust"],"backgroundTag":"gitlab-auto-merge-error","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}