gitbutlerapp/gitbutler · error

Failed to enable merge request auto-merge

Error message

Failed to enable merge request auto-merge: {status} - {error_text}

What it means

set_merge_request_auto_merge calls the GitLab auto-merge endpoint (POST .../merge_trains or /auto_merge per instance version). A non-2xx response bails with the status and GitLab's error body. GitLab rejects auto-merge when the MR is not currently mergeable or when merge trains are not enabled for the project.

Solutions

  1. Inspect the embedded error_text: 400/405 commonly means merge trains are disabled or the merge_status is `not_open`/`checking`.
  2. Verify the MR's `merge_status` is `can_be_merged` and pipeline status before enabling auto-merge; retry after pipeline finishes.
  3. Use the correct auto_merge_strategy for the project config (`merge_train_when_pipeline_succeeds` only with merge trains enabled).
  4. Check the user has Developer+ role and the target branch protection allows their merges.
  5. Treat 404/405 on old GitLab instances as 'feature unsupported' and fall back to polling-and-merging.

Example fix

// before
client.set_merge_request_auto_merge(project_id, iid, strategy).await?;
// after
let mr = client.get_merge_request(project_id, iid).await?;
if mr.merge_status == "can_be_merged" {
    client.set_merge_request_auto_merge(project_id, iid, strategy).await?;
} else {
    // wait for pipeline / discussions to resolve first
}
Defensive patterns

Strategy: try-catch

Validate before calling

let mr = client.get_merge_request(project_id, mr_iid).await?;
if mr.merge_status != "can_be_merged" || mr.draft || mr.blocking_discussions_resolved != Some(true) {
    return Err(anyhow!("MR not eligible for auto-merge yet"));
}

Try / catch

match client.set_merge_request_auto_merge(project_id, iid, strategy).await {
    Err(e) if e.to_string().contains("405") || e.to_string().contains("400") => {
        // merge trains disabled or MR not open: fall back to polling + merge
    }
    Err(e) => return Err(e),
    Ok(()) => {}
}

Prevention

When it happens

Trigger: Enabling auto-merge on an MR whose pipeline hasn't started/failed `merge_status` checks, when the project has merge trains disabled (GitLab returns 400/405), when the MR has unresolved discussions, or when the token lacks merge permission. Also triggered when the auto-merge strategy payload is invalid for the instance's GitLab version.

Common situations: Project recently switched between merge trains and simple auto-merge, so the chosen strategy is no longer valid; MR not yet ready when the client fires the call; free-tier project without merge-train feature; race where the MR gets merged before auto-merge is set.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/67654c97375efb07. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-gitlab/src/client.rs:566

                auto_merge: bool,
            }

            let url = format!(
                "{}/projects/{}/merge_requests/{}/merge",
                self.base_url, project_id, params.mr_iid
            );

            let response = self
                .client
                .put(&url)
                .json(&EnableAutoMergeBody { auto_merge: true })
                .send()
                .await?;

            if !response.status().is_success() {
                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}");
        }

View on GitHub (pinned to 58e5313667)