gitbutlerapp/gitbutler · error · anyhow::Error

Failed to update merge request: {status} - {error_text}

Error message

Failed to update merge request: {status} - {error_text}

What it means

Thrown by GitLabClient::update_merge_request when the PUT to /projects/:id/merge_requests/:iid fails. Unlike most bails in this client it also forwards the response body (error_text), which contains GitLab's validation message, e.g. `{"message":"Target branch does not exist"}` — read it before doing anything else. The body updates title, description, target_branch and state_event (close/reopen) in one call.

Source

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

                .await?;
            Some(update_draft_state_in_title(title, mr.draft))
        } else {
            None
        };

        let body = UpdateMergeRequestBody {
            title: title.as_deref(),
            description: params.description,
            target_branch: params.target_branch,
            state_event: params.state_event,
        };

        let response = self.client.put(&url).json(&body).send().await?;

        if !response.status().is_success() {
            let status = response.status();
            let error_text = response.text().await.unwrap_or_default();
            bail!("Failed to update merge request: {status} - {error_text}");
        }

        let mr: GitLabMergeRequest = response.json().await?;
        Ok(mr.into())
    }

    pub async fn merge_merge_request(&self, params: &MergeMergeRequestParams) -> Result<()> {
        #[derive(Serialize)]
        struct MergeMergeRequestBody {
            #[serde(skip_serializing_if = "Option::is_none")]
            squash: Option<bool>,
        }

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

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Read the {error_text} portion of the message — GitLab names the exact invalid field
  2. If target_branch is rejected, verify the branch exists (GET /projects/:id/repository/branches/:name) and re-run with the correct name
  3. Re-fetch the MR (get_merge_request) to confirm it still exists and is not already in the requested state before re-sending
  4. For 403, check the account's role on the project (Developer+ to edit MRs)
  5. Retry once after `but config forge auth` if 401

Example fix

// before
client.update_merge_request(&params).await?;

// after: guard the two common validation failures before the PUT
let mr = client.get_merge_request(project_id.clone(), mr_iid).await?;
if let Some(target) = params.target_branch {
    ensure!(target.is_empty().not(), "target branch name must not be empty");
    ensure!(mr.state == "opened", "cannot update a {} MR", mr.state);
}
client.update_merge_request(&params).await?;
Defensive patterns

Strategy: validation

Validate before calling

// Reject the two common invalid payloads before the PUT
if let Some(branch) = params.target_branch {
    ensure!(!branch.trim().is_empty(), "target branch must not be empty");
}
ensure!(matches!(params.state_event, None | Some("close") | Some("reopen")), "invalid state_event");
client.update_merge_request(&params).await?;

Try / catch

if let Err(err) = client.update_merge_request(&params).await {
    let msg = err.to_string();
    if msg.contains("Target branch") { /* re-fetch branches, prompt user to pick again */ }
    else { return Err(err); }
}

Prevention

When it happens

Trigger: PUT with target_branch set to a branch that no longer exists in the target project; state_event close/reopen on an MR in a conflicting state; title/description rejected by validation (e.g., empty title); 403 when the account lacks Developer rights to edit the MR; editing an MR that was just deleted (404).

Common situations: Retargeting an MR to a branch that was renamed or deleted by CI; automated title/description updates racing with the user closing the MR; permissions downgraded after the MR was opened; updating an MR on a fork whose upstream target moved.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/41def527890e5a2c. Report an issue: GitHub.