gitbutlerapp/gitbutler · error · anyhow::Error
Failed to set merge request draft state: {status} - {error_t
Error message
Failed to set merge request draft state: {status} - {error_text} What it means
Thrown by GitLabClient::set_merge_request_draft_state when the underlying MR title update (PUT /projects/:id/merge_requests/:iid with only the title field) fails. The function works by rewriting the title: it prepends or strips the `Draft: ` prefix (see update_draft_state_in_title) rather than using a dedicated endpoint. The bail includes both the HTTP status and GitLab's response body, so the validation reason is usually readable in the message.
Source
Thrown at crates/but-gitlab/src/client.rs:468
title: &'a str,
}
let url = format!(
"{}/projects/{}/merge_requests/{}",
self.base_url, project_id, params.mr_iid
);
let response = self
.client
.put(&url)
.json(&UpdateMergeRequestBody { title: &next_title })
.send()
.await?;
if !response.status().is_success() {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
bail!("Failed to set merge request draft state: {status} - {error_text}");
}
Ok(())
}
pub async fn set_merge_request_auto_merge(
&self,
params: &SetMergeRequestAutoMergeParams,
) -> Result<()> {
let project_id = params.project_id.clone();
if params.enabled {
#[derive(Serialize)]
struct EnableAutoMergeBody {
auto_merge: bool,
}
let url = format!(
"{}/projects/{}/merge_requests/{}/merge",View on GitHub (pinned to caf1f223d3)
Solutions
- Inspect error_text in the message — GitLab states which title validation failed
- Re-fetch the MR and skip the toggle if it is already in the desired state (title already has/lacks the prefix)
- Truncate or reject titles longer than ~245 chars before adding the Draft: prefix
- For 404, drop the stale MR from local state; for 403, check the account can edit the MR
Example fix
// before
client.set_merge_request_draft_state(¶ms).await?;
// after: no-op when already in the desired state, and pre-validate the title length
let mr = client.get_merge_request(params.project_id.clone(), params.mr_iid).await?;
let wants_draft = params.draft; // true = mark as draft
let is_draft = mr.title.starts_with("Draft: ");
if is_draft != wants_draft {
ensure!(mr.title.len() + 7 <= 255, "title too long to toggle draft prefix");
client.set_merge_request_draft_state(¶ms).await?;
} Defensive patterns
Strategy: validation
Validate before calling
// No-op when already in the desired state; guard title length before prefixing
let mr = client.get_merge_request(params.project_id.clone(), params.mr_iid).await?;
let is_draft = mr.title.starts_with("Draft: ");
ensure!(is_draft != params.draft, "nothing to toggle");
ensure!(mr.title.len() + "Draft: ".len() <= 255, "title too long for draft prefix");
client.set_merge_request_draft_state(¶ms).await?; Try / catch
if let Err(err) = client.set_merge_request_draft_state(¶ms).await {
if err.to_string().contains("404") { /* MR deleted meanwhile; refresh view */ }
else { return Err(err); }
} Prevention
- Never toggle draft state from stale cached titles — always re-fetch first
- Enforce a UI-side title length limit below 255 chars so the prefix always fits
When it happens
Trigger: Toggling draft state on an MR that was deleted (404); the stripped/rewritten title becomes empty or invalid and GitLab rejects it with 400; account without edit rights (403); title already at GitLab's 255-character limit so adding 'Draft: ' overflows validation; expired token (401).
Common situations: Toggle races with the user deleting or closing the MR in the web UI; very long MR titles that exceed the limit once the prefix is added; automated 'mark ready' flows running against stale MR state.
Related errors
- Failed to get merge request: {}
- Failed to get MR merge status: {}
- Failed to update merge request: {status} - {error_text}
- Failed to merge merge request: {}
- Failed to enable merge request auto-merge: {status} - {error
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/eafc323cc2d1f9b3.
Report an issue: GitHub.