gitbutlerapp/gitbutler · error · anyhow::Error
Bitbucket does not support reopening a declined pull request
Error message
Bitbucket does not support reopening a declined pull request via the API.
What it means
In `update_review()`, the Bitbucket arm rejects `ReviewState::Open` up front: Bitbucket Cloud's API cannot reopen a declined PR through the endpoints used here (closing maps to `pr::decline`, edits map to `pr::update`). The check runs before any HTTP call, so the PR is left untouched.
Source
Thrown at crates/but-forge/src/review.rs:2013
// a `state` field. Map the forge-agnostic ReviewState onto that.
let state_event = state.as_ref().map(|s| s.as_gitlab_state_event());
let params = but_gitlab::UpdateMergeRequestParams {
project_id,
mr_iid,
title: title.as_deref(),
description: body.as_deref(),
target_branch: target_base.as_deref(),
state_event,
};
but_gitlab::mr::update(preferred_account, params, storage).await?;
Ok(())
}
ForgeName::Bitbucket => {
let preferred_account = preferred_forge_user
.as_ref()
.and_then(|user| user.bitbucket());
if matches!(state, Some(ReviewState::Open)) {
return Err(anyhow::anyhow!(
"Bitbucket does not support reopening a declined pull request via the API."
));
}
if title.is_some() || body.is_some() || target_base.is_some() {
let id = review_number
.try_into()
.context("PR: Failed to cast usize to i64, somehow")?;
let params = but_bitbucket::UpdatePullRequestParams {
workspace: owner,
repo_slug: repo,
id,
title: title.as_deref(),
description: body.as_deref(),
target_branch: target_base.as_deref(),
};
but_bitbucket::pr::update(preferred_account, params, storage).await?;
}
if matches!(state, Some(ReviewState::Closed)) {View on GitHub (pinned to caf1f223d3)
Solutions
- Don't offer 'reopen' for declined Bitbucket PRs — hide the action when the forge is Bitbucket.
- Create a new PR from the same branches instead of reopening the declined one.
- Pass `state: None` when you only want to edit title/description/target branch on Bitbucket.
- If reopening is a hard requirement, call Bitbucket's REST API outside but-forge.
Example fix
// before
update_review(&user, &info, number,
ReviewUpdatePayload { state: Some(ReviewState::Open), ..Default::default() },
&storage).await?;
// after
if matches!(info.forge, ForgeName::Bitbucket)
&& matches!(payload.state, Some(ReviewState::Open))
{
// reopening a declined PR is unsupported; offer 'new PR from branch' instead
return Ok(());
}
update_review(&user, &info, number, payload, &storage).await?; Defensive patterns
Strategy: validation
Validate before calling
use but_forge::forge::ForgeName;
use but_forge::review::ReviewState;
if matches!(info.forge, ForgeName::Bitbucket)
&& matches!(payload.state, Some(ReviewState::Open))
{
// hide 'reopen'; Bitbucket declined PRs cannot reopen via this API
} Type guard
fn reopen_supported(forge: &but_forge::forge::ForgeName) -> bool {
!matches!(forge, ForgeName::Bitbucket)
} Try / catch
match update_review(&user, &info, number, payload, &storage).await {
Ok(()) => {}
Err(e) if e.to_string().contains("does not support reopening") => {
// surface 'create a new PR from the branch' guidance instead
}
Err(e) => return Err(e),
} Prevention
- Keep per-forge state-transition tables: Bitbucket maps Closed to decline and cannot reopen.
- For Bitbucket, treat 'declined' as terminal in the UI and offer 'new PR from branch' instead of 'reopen'.
- Send edits with `state: None` on Bitbucket to avoid tripping the guard.
When it happens
Trigger: Calling `update_review` with `forge: bitbucket` and `ReviewUpdatePayload.state == Some(ReviewState::Open)` — typically a 'Reopen' action on a declined Bitbucket PR. Title/body/target-base edits and `Some(ReviewState::Closed)` (decline) proceed normally.
Common situations: UIs reusing GitHub semantics ('reopen after close') across forges; users expecting declined Bitbucket PRs to be reopenable; automations that normalize state transitions per forge.
Related errors
- Repository labels for forge {forge:?} are not implemented ye
- Review labels for forge {forge:?} are not implemented yet.
- Review requests for forge {forge:?} are not implemented yet.
- Updating pull requests for forge {forge:?} is not implemente
- handled above
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/9a9ba2b6a5b8d215.
Report an issue: GitHub.