{"record":{"id":"c06e040c8d210143","repo":"gitbutlerapp/gitbutler","slug":"bitbucket-repository-workspace-repo-slug-is","errorCode":null,"errorMessage":"Bitbucket repository '{workspace}/{repo_slug}' is inaccessible to this token, or the token is missing required scope `read:repository:bitbucket`","messagePattern":"Bitbucket repository '(.+?)/(.+?)' is inaccessible to this token, or the token is missing required scope `read:repository:bitbucket`","errorType":"http","errorClass":"HttpStatusError","httpStatus":null,"severity":"error","filePath":"crates/but-bitbucket/src/client.rs","lineNumber":614,"sourceCode":"        workspace: &str,\n        repo_slug: &str,\n    ) -> anyhow::Error {\n        if let Err(err) = self.get_authenticated().await {\n            if let Some(http_err) = err.downcast_ref::<HttpStatusError>() {\n                return match http_err.status {\n                    reqwest::StatusCode::UNAUTHORIZED => {\n                        err.context(\"Bitbucket credentials are invalid or expired\")\n                    }\n                    reqwest::StatusCode::FORBIDDEN => err.context(\n                        \"Bitbucket API token is missing required scope `read:user:bitbucket`\",\n                    ),\n                    _ => err.context(\"Failed to verify Bitbucket credentials\"),\n                };\n            }\n            return err.context(\"Failed to verify Bitbucket credentials\");\n        }\n\n        anyhow::Error::new(HttpStatusError { status }).context(format!(\n            \"Bitbucket repository '{workspace}/{repo_slug}' is inaccessible to this token, or the token is missing required scope `read:repository:bitbucket`\"\n        ))\n    }\n}\n\npub(crate) fn resolve_account(\n    preferred_account: Option<&crate::BitbucketAccountIdentifier>,\n    storage: &but_forge_storage::Controller,\n) -> Result<crate::BitbucketAccountIdentifier, anyhow::Error> {\n    let known_accounts = crate::token::list_known_bitbucket_accounts(storage)?;\n    let Some(default_account) = known_accounts.first() else {\n        bail!(\n            \"No authenticated Bitbucket users found.\\nRun 'but config forge auth' to authenticate with Bitbucket.\"\n        );\n    };\n    let account = if let Some(account) = preferred_account {\n        if known_accounts.contains(account) {\n            account","sourceCodeStart":596,"sourceCodeEnd":632,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/2497b8007aa4a1922dae9a805b32ffe5b5037785/crates/but-bitbucket/src/client.rs#L596-L632","documentation":"Terminal message of classify_repository_access_error in but-bitbucket: the repository endpoint denied access and the /user probe could not narrow the cause (a successful /user proves the token itself is valid; a failed one gets its own context). The remaining explanations: this token cannot see {workspace}/{repo_slug}, or it lacks the read:repository:bitbucket scope required by GET /repositories/{ws}/{slug}. Bitbucket deliberately hides missing-scope from no-access, so both are listed.","triggerScenarios":"Fetching checks or PR data with a token that has no access to the repository (404 masquerading as denial); a token created without read:repository:bitbucket (403); workspace or repo_slug typo; private repository in a workspace the token's account does not belong to.","commonSituations":"Workspace-scoped API keys; repository renamed after its URL was copied; user has several workspaces and the token was issued under the wrong one.","solutions":["Grant read:repository:bitbucket to the token (plus read:user:bitbucket), then re-add it","Confirm workspace and slug exactly as in the browser URL and retype them","Check the token's account is a member of the workspace that owns the repository","Test the raw endpoint: curl -s -o /dev/null -w '%{http_code}' -H 'Authorization: Bearer $TOKEN' https://api.bitbucket.org/2.0/repositories/<ws>/<slug>"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Pre-flight right after token entry: the repository endpoint must answer 200\ncurl -s -o /dev/null -w '%{http_code}' \\\n  -H 'Authorization: Bearer $TOKEN' \\\n  https://api.bitbucket.org/2.0/repositories/<workspace>/<repo_slug>\n// 200 = access OK; 401/403 = scope or auth problem; 404 = wrong slug or no access","typeGuard":"fn is_repository_access_error(err: &anyhow::Error) -> bool {\n    err.chain().any(|c| c.to_string().contains(\"read:repository:bitbucket\"))\n}","tryCatchPattern":"if let Err(err) = client.list_checks_for_ref(ws, slug, ref_).await {\n    if is_repository_access_error(&err) {\n        // stop the onboarding flow and ask for scope fix / correct slug\n        return Err(err.context(\"fix token scopes or workspace/slug, then retry\"));\n    }\n    return Err(err);\n}","preventionTips":["Validate repository access immediately when a token is entered, not at first PR/check use","Keep a scope checklist per forge feature and assert it during setup","Copy workspace/slug from the repository URL, never from memory"],"tags":["bitbucket","authorization","oauth-scope","repository-access","rust"],"backgroundTag":"insufficient-token-scopes","analyzedSha":"2497b8007aa4a1922dae9a805b32ffe5b5037785","analyzedAt":"2026-08-17T00:30:25.648Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}