{"record":{"id":"ed5e9a26543f75a7","repo":"gitbutlerapp/gitbutler","slug":"failed-to-verify-bitbucket-repository-access","errorCode":null,"errorMessage":"Failed to verify Bitbucket repository access","messagePattern":"Failed to verify Bitbucket repository access","errorType":"http","errorClass":"HttpStatusError","httpStatus":null,"severity":"error","filePath":"crates/but-bitbucket/src/client.rs","lineNumber":543,"sourceCode":"        );\n        let response = self.client.get(&url).send().await?;\n        let status = response.status();\n        if status == reqwest::StatusCode::NOT_FOUND {\n            let repository_status = self.repository_status(workspace, repo_slug).await?;\n            if repository_status.is_success() {\n                return Ok(None);\n            }\n            if matches!(\n                repository_status,\n                reqwest::StatusCode::UNAUTHORIZED\n                    | reqwest::StatusCode::FORBIDDEN\n                    | reqwest::StatusCode::NOT_FOUND\n            ) {\n                return Err(self\n                    .classify_repository_access_error(repository_status, workspace, repo_slug)\n                    .await);\n            }\n            return Err(anyhow::Error::new(HttpStatusError {\n                status: repository_status,\n            })\n            .context(\"Failed to verify Bitbucket repository access\"));\n        }\n        if matches!(\n            status,\n            reqwest::StatusCode::UNAUTHORIZED | reqwest::StatusCode::FORBIDDEN\n        ) {\n            return Err(self\n                .classify_repository_access_error(status, workspace, repo_slug)\n                .await);\n        }\n        if !status.is_success() {\n            bail!(\"Bitbucket request failed: {status}\");\n        }\n\n        let page: Paginated<BitbucketApiBuildStatus> = response.json().await?;\n        let mut statuses = page.values;","sourceCodeStart":525,"sourceCodeEnd":561,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/2497b8007aa4a1922dae9a805b32ffe5b5037785/crates/but-bitbucket/src/client.rs#L525-L561","documentation":"Fallback error from but-bitbucket's list_checks_for_ref: after a 404 on the commit-statuses endpoint, the client re-probes GET /repositories/{workspace}/{repo_slug} to distinguish 'reference not found' from 'repository inaccessible'. When that probe returns a status outside 401/403/404 (those get richer classification via classify_repository_access_error), the raw status is wrapped in HttpStatusError with this generic context. In practice it indicates an unexpected condition on Bitbucket's side or the network path: a 5xx outage, 429 rate limit, or an intermediary such as a proxy.","triggerScenarios":"list_checks_for_ref gets a 404 for the ref, then the repository probe returns 429 or 5xx because of burst polling or a Bitbucket incident; a corporate proxy answers 502/503; a rare non-classified 4xx from the repository endpoint.","commonSituations":"CI jobs polling build checks in tight loops tripping rate limits; Bitbucket Cloud status-page incidents; proxied or NATed egress environments rewriting responses.","solutions":["Check https://status.bitbucketcloud.net for an ongoing incident when the status is 5xx","Back off and retry - 429 responses carry Retry-After; wait that long before re-running","Inspect proxy/firewall configuration if the status looks like 502/503 from an intermediary","Re-run with tracing/debug logging enabled to capture the exact status code that reached this branch"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"fn is_transient_http_error(err: &anyhow::Error) -> bool {\n    matches!(\n        err.downcast_ref::<HttpStatusError>().map(|e| e.status),\n        Some(s) if s.is_server_error() || s == reqwest::StatusCode::TOO_MANY_REQUESTS\n    )\n}","tryCatchPattern":"let mut attempt = 0u32;\nloop {\n    match client.list_checks_for_ref(ws, slug, reference).await {\n        Ok(statuses) => break Ok(statuses),\n        Err(err) if is_transient_http_error(&err) && attempt < 3 => {\n            tokio::time::sleep(std::time::Duration::from_secs(2u64.pow(attempt))).await;\n            attempt += 1;\n        }\n        Err(err) => break Err(err),\n    }\n}","preventionTips":["Cache check-query results instead of polling in tight loops; add jitter between polls","Watch Bitbucket's status feed and pause automation during incidents","Distinguish 5xx/429 from 4xx in logs so transient vs permanent failures are visible"],"tags":["bitbucket","http-status","transient","rate-limit","rust"],"backgroundTag":"external-service-error-5xx","analyzedSha":"2497b8007aa4a1922dae9a805b32ffe5b5037785","analyzedAt":"2026-08-17T00:30:25.648Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}