gitbutlerapp/gitbutler · error · anyhow::Error

Bitbucket request failed: {status}

Error message

Bitbucket request failed: {status}

What it means

Raised while fetching build statuses for a commit after two earlier guards already ran: repository access was verified (failure returns early with a 'Failed to verify Bitbucket repository access' context) and 401/403 were reclassified by classify_repository_access_error(). This bail therefore catches every other non-success status from the build-status endpoint.

Source

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

                return Err(self
                    .classify_repository_access_error(repository_status, workspace, repo_slug)
                    .await);
            }
            return Err(anyhow::Error::new(HttpStatusError {
                status: repository_status,
            })
            .context("Failed to verify Bitbucket repository access"));
        }
        if matches!(
            status,
            reqwest::StatusCode::UNAUTHORIZED | reqwest::StatusCode::FORBIDDEN
        ) {
            return Err(self
                .classify_repository_access_error(status, workspace, repo_slug)
                .await);
        }
        if !status.is_success() {
            bail!("Bitbucket request failed: {status}");
        }

        let page: Paginated<BitbucketApiBuildStatus> = response.json().await?;
        let mut statuses = page.values;
        if let Some(next) = page.next {
            statuses.extend(self.get_paginated(next).await?);
        }
        Ok(Some(
            statuses
                .into_iter()
                .map(BitbucketBuildStatus::from_api)
                .collect(),
        ))
    }

    async fn repository_status(
        &self,
        workspace: &str,

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Treat 429/5xx as transient: back off and retry the status lookup
  2. For 404, confirm the commit sha/key used for the status query actually exists in that repo
  3. If it persists, capture the status code and check Bitbucket status page / API changelog
  4. Rate-limit your own polling loop (e.g. exponential backoff between checks)
Defensive patterns

Strategy: retry

Validate before calling

null // nothing to validate client-side beyond what the client already verifies; throttle instead
static POLL: Lazy<RateLimiter> = Lazy::new(|| RateLimiter::per_minute(30));
POLL.wait().await;

Try / catch

// transient-oriented retry, since 401/403 are already classified elsewhere
for attempt in 0..4 {
    match client.build_statuses(ws, slug, sha).await {
        Err(e) if embedded_status(&e).is_some_and(|s| s.as_u16() == 429 || s.is_server_error())
            => { tokio::time::sleep(backoff(attempt)).await; }
        r => return r.map_err(Into::into),
    }
}

Prevention

When it happens

Trigger: Calling the build-status lookup and receiving something other than 2xx/401/403: typically 404 (no build statuses or unknown commit key), 429 rate limiting, or a 5xx from Bitbucket's pipeline-status API.

Common situations: Polling build status in a loop right after pushing (status key not yet registered); aggressive CI polling tripping rate limits; Bitbucket Cloud incidents.

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@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/5631e9c116ce3f45. Report an issue: GitHub.