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
- Regenerate the Bitbucket token and ensure the `read:repository:bitbucket` scope is granted.
- Verify the token's user has at least Read permission on `{workspace}/{repo_slug}` in Bitbucket settings.
- Confirm the workspace and repo slug in the target configuration are correct and the repo still exists there.
- 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
- Always create Bitbucket tokens with `read:repository:bitbucket` plus the write scopes you need.
- Confirm the token's user still has repo access after team/workspace changes.
- Re-authenticate after rotating tokens or changing app-password scopes.
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
- NOT_AUTHENTICATED
- Bitbucket does not support reopening a declined pull…
- Bitbucket pagination exceeded the
- Bitbucket request failed
- Bitbucket request failed
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)