gitbutlerapp/gitbutler · error · HttpStatusError

Bitbucket repository

Error message

Bitbucket repository '{workspace}/{repo_slug}' is inaccessible to this token, or the token is missing required scope `read:repository:bitbucket`

What it means

Raised by `classify_repository_access_error` when Bitbucket rejects repository access with an access-related status. It tells you the token authenticates but cannot read the repository — either the repo is private/inaccessible to that token's user, or the token lacks the `read:repository:bitbucket` scope.

Solutions

  1. Regenerate the Bitbucket token and ensure the `read:repository:bitbucket` scope is granted.
  2. Verify the token's user has at least Read permission on `{workspace}/{repo_slug}` in Bitbucket settings.
  3. Confirm the workspace and repo slug in the target configuration are correct and the repo still exists there.
  4. Re-authenticate (e.g. re-run `but login`) so the stored credentials match the intended account.

Example fix

// before (app password scopes)
scopes = ["write:pullrequest:bitbucket"]
// after
scopes = ["write:pullrequest:bitbucket", "read:repository:bitbucket"]
Defensive patterns

Strategy: validation

Validate before calling

// before connecting, probe access with a cheap read
let me = reqwest::get("https://api.bitbucket.org/2.0/user").bearer_auth(token).send().await?;
let repo = reqwest::get(format!("https://api.bitbucket.org/2.0/repositories/{workspace}/{repo_slug}")).bearer_auth(token).send().await?;
if !repo.status().is_success() { return Err("token cannot read this repo; check scope read:repository:bitbucket".into()); }

Try / catch

match client.list_checks_for_ref(&r).await {
    Err(e) if e.to_string().contains("inaccessible to this token") => {
        eprintln!("Re-create your Bitbucket token with scope read:repository:bitbucket");
        prompt_relogin();
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling `list_checks_for_ref` (or any code path that first verifies repo access) with a token whose user is not a member of the workspace, whose permission on the repo is below read, or whose scopes do not include `read:repository:bitbucket`; also for 404s where Bitbucket hides private repos to look nonexistent.

Common situations: Creating an API token / app password without ticking the Repository: Read scope; rotating to a new token with fewer scopes; the repository was moved to another workspace or made private; using a personal token of a user removed from the workspace.

Understand the failure class

Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — this error's family across 31 libraries.

Related errors


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

Appendix: source

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

        workspace: &str,
        repo_slug: &str,
    ) -> anyhow::Error {
        if let Err(err) = self.get_authenticated().await {
            if let Some(http_err) = err.downcast_ref::<HttpStatusError>() {
                return match http_err.status {
                    reqwest::StatusCode::UNAUTHORIZED => {
                        err.context("Bitbucket credentials are invalid or expired")
                    }
                    reqwest::StatusCode::FORBIDDEN => err.context(
                        "Bitbucket API token is missing required scope `read:user:bitbucket`",
                    ),
                    _ => err.context("Failed to verify Bitbucket credentials"),
                };
            }
            return err.context("Failed to verify Bitbucket credentials");
        }

        anyhow::Error::new(HttpStatusError { status }).context(format!(
            "Bitbucket repository '{workspace}/{repo_slug}' is inaccessible to this token, or the token is missing required scope `read:repository:bitbucket`"
        ))
    }
}

/// Marks credential lookups that came up empty, so consumers can tell "the
/// user is not authenticated" apart from a failing forge and e.g. keep
/// serving cached data instead of surfacing an error.
pub(crate) const NOT_AUTHENTICATED: but_error::Context = but_error::Context::new_static(
    but_error::Code::ForgeNotAuthenticated,
    "Not authenticated with Bitbucket. Connect your account under Settings → Integrations.",
);

pub(crate) fn resolve_account(
    preferred_account: Option<&crate::BitbucketAccountIdentifier>,
    storage: &but_forge_storage::Controller,
) -> Result<crate::BitbucketAccountIdentifier, anyhow::Error> {
    let known_accounts = crate::token::list_known_bitbucket_accounts(storage)?;

View on GitHub (pinned to 58e5313667)