gitbutlerapp/gitbutler · error
Failed to fetch repository
Error message
Failed to fetch repository: {status} - {error_text} What it means
Thrown by fetch_repo when the GET for repository metadata returns a non-success status. The message includes the HTTP status and Bitbucket's response body. Notably, if the failure is an access problem this path can instead be pre-empted by classify_repository_access_error in callers like list_checks_for_ref; this bail fires for other non-success statuses.
Solutions
- Verify workspace and repo-slug spelling and that the repository still exists
- Check the Bitbucket token is valid and has read access to the repository
- Handle 5xx with a retry/backoff
- Use the repository access error classifier for 401/403/404 cases to produce a clearer user-facing message
Example fix
// before
let repo = client.fetch_repo(workspace, repo_slug).await?;
// after
let repo = client.fetch_repo(workspace, repo_slug).await
.map_err(|e| anyhow::anyhow!("Bitbucket repo {workspace}/{repo_slug}: {e:#}"))?; Defensive patterns
Strategy: validation
Validate before calling
anyhow::ensure!(!workspace.is_empty() && !repo_slug.is_empty(), "workspace/slug required"); // optionally probe access first let _ = client.fetch_repo_permission(workspace, repo_slug).await?;
Try / catch
match client.fetch_repo(ws, slug).await {
Err(e) if e.to_string().contains("404") => anyhow::bail!("repo {ws}/{slug} not found — check spelling"),
Err(e) if e.to_string().contains("401") || e.to_string().contains("403") => anyhow::bail!("insufficient access: {e}"),
other => other?,
} Prevention
- Validate workspace/slug strings early
- Keep tokens valid and scoped for repository read
- Detect renamed/moved repos via periodic revalidation
- Retry 5xx with backoff
When it happens
Trigger: Calling fetch_repo with a workspace/repo_slug that does not exist (404), with an invalid token (401), with a token lacking read access (403), or during a Bitbucket outage (5xx).
Common situations: Typo'd workspace or repository slug; repository renamed or moved; app password rotated/expired; private repo accessed with a public-scoped token.
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
- Bitbucket request failed
- Bitbucket request failed
- Failed to decline pull request
- Failed to merge pull request
- Failed to update pull request
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/8842633f4a9feaa1.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-bitbucket/src/client.rs:500
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
bail!("Failed to decline pull request: {status} - {error_text}");
}
Ok(())
}
pub async fn fetch_repo(&self, workspace: &str, repo_slug: &str) -> Result<BitbucketRepo> {
let url = format!(
"{}/repositories/{}/{}",
self.base_url,
urlencoding::encode(workspace),
urlencoding::encode(repo_slug),
);
let response = self.client.get(&url).send().await?;
if !response.status().is_success() {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
bail!("Failed to fetch repository: {status} - {error_text}");
}
let repo: BitbucketApiRepository = response.json().await?;
// The caller's permission level lives behind a separate endpoint.
let permission = self
.fetch_repo_permission(workspace, repo_slug)
.await
.ok()
.flatten();
Ok(BitbucketRepo {
is_fork: repo.parent.is_some(),
is_private: repo.is_private,
permission,
})
}
/// Resolve the authenticated user's permission (`admin`/`write`/`read`) on a repo.View on GitHub (pinned to 58e5313667)