gitbutlerapp/gitbutler · error · HttpStatusError

Failed to create pull request

Error message

Failed to create pull request: {status} - {error_text}

What it means

Raised by the Bitbucket API client when a POST to create a pull request returns a non-2xx status. The error wraps `HttpStatusError` with a context string containing the HTTP status and the raw response body, so the caller sees exactly why Bitbucket rejected the PR creation.

Solutions

  1. Read the `error_text` in the context message — Bitbucket includes the field-level validation reason there.
  2. Verify the token has the `write:pullrequest:bitbucket` scope and has not expired.
  3. Confirm the workspace/repo slug and that both the source and destination branches exist on the remote.
  4. If the status is 429 or 5xx, wait and retry the PR creation.

Example fix

// before
match client.create_pull_request(&title, &head, &base).await {
    Err(e) => panic!("{e}"),
    Ok(pr) => println!("{}", pr.url),
}
// after
match client.create_pull_request(&title, &head, &base).await {
    Err(e) if e.to_string().contains("400") => eprintln!("Invalid PR payload: {e:#}"),
    Err(e) => eprintln!("PR creation failed: {e:#}"),
    Ok(pr) => println!("{}", pr.url),
}
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-flight: verify token can see the repo and branches exist
let head_ok = client.repo_exists(workspace, repo_slug).await?;
let base_ok = client.branch_exists(workspace, repo_slug, base).await?;

Try / catch

match client.create_pull_request(&title, &head, &base).await {
    Err(e) if e.to_string().contains("429") => backoff_and_retry().await,
    Err(e) => log::error!("PR create failed: {e:#}"),
    Ok(pr) => Ok(pr),
}

Prevention

When it happens

Trigger: Calling `create_pull_request` -> `send_pr_create` when Bitbucket responds with 400 (invalid PR payload), 401/403 (bad token or insufficient scope), 404 (wrong workspace/repo slug), or 429/5xx (server-side issues).

Common situations: Stale or revoked Bitbucket app password/API token; missing `write:pullrequest:bitbucket` scope; typo'd workspace or repo slug; source or destination branch name that does not exist or contains invalid characters; Bitbucket rate limiting during CI bursts.

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


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/247f6e1566cbea39. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-bitbucket/src/client.rs:275

            close_source_branch: true,
            source,
            destination: DestinationBody {
                branch: BranchBody {
                    name: params.target_branch,
                },
            },
            reviewers: reviewer_account_ids
                .iter()
                .map(|account_id| ReviewerBody { account_id })
                .collect(),
        };

        let response = self.client.post(&url).json(&body).send().await?;
        if !response.status().is_success() {
            let status = response.status();
            let error_text = response.text().await.unwrap_or_default();
            return Err(
                anyhow::Error::new(HttpStatusError { status }).context(format!(
                    "Failed to create pull request: {status} - {error_text}"
                )),
            );
        }
        let pr: BitbucketApiPullRequest = response.json().await?;
        Ok(pr.into())
    }

    /// Bitbucket does not apply default reviewers to API-created pull requests,
    /// so fetch the effective set (repository- plus project-level) minus the
    /// author, whom Bitbucket rejects as a reviewer.
    /// Best-effort: a failed lookup yields no reviewers.
    async fn default_reviewers_for_new_pr(&self, workspace: &str, repo_slug: &str) -> Vec<String> {
        #[derive(Deserialize)]
        struct DefaultReviewer {
            #[serde(default)]
            user: Option<BitbucketApiUser>,
        }

View on GitHub (pinned to 58e5313667)