gitbutlerapp/gitbutler · error · anyhow::Error
Failed to disable merge request auto-merge: {status} - {erro
Error message
Failed to disable merge request auto-merge: {status} - {error_text} What it means
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.
Source
Thrown at crates/but-gitlab/src/client.rs:516
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
bail!("Failed to enable merge request auto-merge: {status} - {error_text}");
}
return Ok(());
}
let url = format!(
"{}/projects/{}/merge_requests/{}/cancel_merge_when_pipeline_succeeds",
self.base_url, project_id, params.mr_iid
);
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 disable merge request auto-merge: {status} - {error_text}");
}
Ok(())
}
pub async fn fetch_project(&self, project_id: GitLabProjectId) -> Result<GitLabProject> {
self.fetch_project_by_path(project_id.to_string()).await
}
pub async fn fetch_project_by_numeric_id(&self, project_id: i64) -> Result<GitLabProject> {
self.fetch_project_by_path(project_id.to_string()).await
}
async fn fetch_project_by_path(&self, project_id: String) -> Result<GitLabProject> {
#[derive(Deserialize)]
struct GitLabApiProject {
id: i64,
path_with_namespace: String,View on GitHub (pinned to caf1f223d3)
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
Example fix
// before
client.set_merge_request_auto_merge(¶ms).await?;
// after: idempotent cancel
if let Err(err) = client.set_merge_request_auto_merge(¶ms).await {
let msg = err.to_string();
if !(msg.contains("406") || msg.contains("404")) {
return Err(err); // 406 = never enabled, 404 = MR gone; both mean 'nothing to cancel'
}
} Defensive patterns
Strategy: fallback
Validate before calling
// Only offer cancel when MR state actually shows pending auto-merge (from a fresh get_merge_request)
ensure!(mr.merge_status_is_auto_merge_pending(), "auto-merge not enabled for MR {}"); // conceptually
client.set_merge_request_auto_merge(¶ms).await?; Try / catch
if let Err(err) = client.set_merge_request_auto_merge(¶ms).await {
let m = err.to_string();
if m.contains("406") || m.contains("404") { /* nothing to cancel — treat as success */ }
else { return Err(err); }
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- Failed to enable merge request auto-merge: {status} - {error
- Failed to get merge request: {}
- Failed to get MR merge status: {}
- Failed to update merge request: {status} - {error_text}
- Failed to merge merge request: {}
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/6130f2f1b1cdfe36.
Report an issue: GitHub.