gitbutlerapp/gitbutler · error · anyhow::Error
Failed to update pull request: {status} - {error_text}
Error message
Failed to update pull request: {status} - {error_text} What it means
Raised by update_pull_request's underlying PUT to /repositories/{workspace}/{repo_slug}/pullrequests/{id} when Bitbucket answers non-2xx; the message carries both the status code and the raw response body (error_text), which usually contains Bitbucket's JSON error detail naming the invalid field or unmet precondition.
Source
Thrown at crates/but-bitbucket/src/client.rs:380
async fn send_pr_update(
&self,
workspace: &str,
repo_slug: &str,
id: i64,
body: &UpdatePullRequestBody<'_>,
) -> Result<BitbucketPullRequest> {
let url = format!(
"{}/repositories/{}/{}/pullrequests/{}",
self.base_url,
urlencoding::encode(workspace),
urlencoding::encode(repo_slug),
id,
);
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 pull request: {status} - {error_text}");
}
let pr: BitbucketApiPullRequest = response.json().await?;
Ok(pr.into())
}
pub async fn update_pull_request(
&self,
params: &UpdatePullRequestParams<'_>,
) -> Result<BitbucketPullRequest> {
let ctx = self
.fetch_pr_edit_context(params.workspace, params.repo_slug, params.id)
.await?;
let body = build_update_body(
&ctx,
params.title,
params.description,
params.target_branch,
None,View on GitHub (pinned to caf1f223d3)
Solutions
- Read {error_text}: Bitbucket names the exact problem (e.g. field validation or state error)
- Re-fetch the PR (list/get) and only call update when values.state == OPEN
- Re-authenticate with 'but config forge auth' if the status is 401/403 so the token gets pullrequest write scope
- On 409/version conflict, re-fetch, re-apply the edit on the fresh version, and retry once
Example fix
// before: blind edit on a possibly-stale PR
client.update_pull_request(¶ms).await?;
// after: refresh state, edit only while OPEN
let pr = client.get_pull_request(ws, slug, id).await?;
if pr.state != "OPEN" { anyhow::bail!("cannot edit {} PR", pr.state); }
client.update_pull_request(¶ms).await?; Defensive patterns
Strategy: try-catch
Validate before calling
// only edit PRs that are currently OPEN
let pr = client.get_pull_request(ws, slug, id).await?;
anyhow::ensure!(pr.state == "OPEN", "PR {id} is {} — not editable", pr.state); Try / catch
match client.update_pull_request(¶ms).await {
Err(e) => {
let msg = e.to_string();
if msg.contains("409") { reapply_on_fresh_version(&client, ¶ms).await?; }
else { return Err(e.context("update PR")); }
}
ok => ok,
} Prevention
- Refresh PR state immediately before editing; never reuse a view older than seconds in busy repos
- Disable the edit button for non-OPEN PRs in the UI
- Keep the raw error_text visible to users — Bitbucket names the invalid field
When it happens
Trigger: Editing title/description/reviewers of a PR that is no longer OPEN (MERGED/DECLINED PRs reject edits), sending an invalid payload (empty title, unknown reviewer UUID), a token without pullrequest write scope, or a 409 from editing a PR whose version moved concurrently.
Common situations: UI holding a stale PR view after someone merged/declined it elsewhere; automation editing PRs after CI merges them; token scopes trimmed after a security review.
Related errors
- Failed to merge pull request: {status} - {error_text}
- Failed to decline pull request: {status} - {error_text}
- Failed to create pull request: {status} - {error_text}
- Bitbucket request failed: {}
- Failed to fetch repository: {status} - {error_text}
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/06a536185c37bb4e.
Report an issue: GitHub.