gitbutlerapp/gitbutler · error
Failed to update pull request
Error message
Failed to update pull request: {} What it means
The GitHub REST PATCH call used to update a pull request (title, body, state, etc.) returned a non-success HTTP status. The library bails with the response body (rendered via response_error) so you can see GitHub's actual rejection reason. It signals the update request itself failed server-side, not a local validation problem.
Solutions
- Read the appended response_error output to see GitHub's concrete reason and fix the request field it names
- Verify the token has repo/pull-requests write scope (classic) or Pull requests: write (fine-grained)
- Confirm the PR number exists in owner/repo (e.g. via gh pr view or get_pull_request)
- Check for 403/429 rate-limit or SSO issues and retry after the rate-limit reset
Example fix
// before
client.update_pull_request(&UpdatePullRequestParams { owner, repo, pr_number: 0, .. }).await?;
// after
if pr_number > 0 {
client.update_pull_request(&UpdatePullRequestParams { owner, repo, pr_number, .. }).await?;
} Defensive patterns
Strategy: try-catch
Validate before calling
// before calling: ensure the PR exists and the token can write let pr = client.get_pull_request(owner, repo, pr_number).await?; assert!(pr_number > 0);
Try / catch
// Rust: match on the error and inspect the embedded GitHub response body
match client.update_pull_request(¶ms).await {
Ok(pr) => pr,
Err(e) if e.to_string().contains("Failed to update pull request") => {
eprintln!("GitHub rejected the update: {e}");
// 403 -> check token scopes; 404 -> check owner/repo/number
return Err(e);
}
Err(e) => return Err(e),
} Prevention
- Grant the token Pull requests: write before automation runs
- Fetch the PR first to confirm it exists and is mutable
- Handle 429/403 rate-limit responses with backoff
- Log the full response_error body so GitHub's reason is never swallowed
When it happens
Trigger: Calling update_pull_request with parameters GitHub rejects: nonexistent PR number, wrong owner/repo, token lacking write access to the repo or PR, closed PRs receiving state changes not allowed, invalid field values, or network/429/5xx responses.
Common situations: Using a fine-grained PAT without pull request write permission; stale PR numbers after rebase/force-push cleanup; typos in owner/repo; hitting rate limits during automation loops; trying to update a PR in an archived repository.
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
- Failed to get PR base repo
- Could not update all PR targets before native stack…
- Failed to add labels
- Failed to add to GitHub stack
- Failed to create GitHub stack
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/8e0bf28dd505be2c.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-github/src/client.rs:1442
state: Option<&'a str>,
}
let url = format!(
"{}/repos/{}/{}/pulls/{}",
self.base_url, params.owner, params.repo, params.pr_number
);
let body = UpdatePullRequestBody {
title: params.title,
body: params.body,
base: params.base,
state: params.state,
};
let response = self.client.patch(&url).json(&body).send().await?;
if !response.status().is_success() {
bail!(
"Failed to update pull request: {}",
response_error(response).await
);
}
let pr: GitHubPullRequest = response.json().await?;
Ok(pr.into())
}
/// Merge a PR.
pub async fn merge_pull_request(&self, params: &MergePullRequestParams<'_>) -> Result<()> {
#[derive(Serialize)]
struct MergePullRequestBody<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
commit_title: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
commit_message: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]View on GitHub (pinned to 58e5313667)