gitbutlerapp/gitbutler · error

Failed to get repo

Error message

Failed to get repo: {}

What it means

GitHubClient::get_repo fetches /repos/{owner}/{repo} to inspect repository metadata (e.g. whether the user can merge a PR). Any non-2xx status from GitHub results in this bail with the HTTP status. Typically means the repo does not exist or is inaccessible with the current credentials.

Solutions

  1. Verify owner/repo with curl -H "Authorization: Bearer $TOKEN" https://api.github.com/repos/{owner}/{repo}
  2. Check token scopes/permissions; regenerate with repo (or fine-grained repository read) access
  3. Watch for 403 with rate-limit headers; wait for reset or use a token with higher limits

Example fix

// before
let repo = client.get_repo("acme", "reppo").await?; // typo
// after
let repo = client.get_repo("acme", "repo").await?; // verified via API
Defensive patterns

Strategy: try-catch

Validate before calling

let status = reqwest::Client::new()
    .get(&format!("https://api.github.com/repos/{owner}/{repo}"))
    .header("Authorization", format!("Bearer {token}"))
    .send().await?.status();
if !status.is_success() { return Err(format!("repo unreachable: {status}")); }

Try / catch

match client.get_repo(owner, repo).await {
    Ok(r) => r,
    Err(e) if e.to_string().contains("Failed to get repo: 403") => {
        // rate-limited or forbidden: back off / check scopes
        ...
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling get_repo with an owner/repo that doesn't exist, is private and the token lacks access, or the API returns 403/404/5xx.

Common situations: Renamed or deleted repository; token missing 'repo' scope for a private repo; typo'd owner/repo; hitting a rate limit (403); corporate proxy blocking api.github.com.

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


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

Appendix: source

Thrown at crates/but-github/src/client.rs:160

        let user: User = response.json().await?;

        Ok(AuthenticatedUser {
            login: user.login,
            avatar_url: user.avatar_url,
            name: user.name,
            email: user.email,
        })
    }

    /// Fetch repo-level metadata (currently just the caller's permissions
    /// and whether the repo is a fork). Used by the desktop to decide
    /// whether the authenticated user can merge a given PR.
    pub async fn get_repo(&self, owner: &str, repo: &str) -> Result<GitHubRepository> {
        let url = format!("{}/repos/{}/{}", self.base_url, owner, repo);
        let response = self.client.get(&url).send().await?;
        if !response.status().is_success() {
            bail!("Failed to get repo: {}", response.status());
        }
        Ok(response.json().await?)
    }

    /// Fetch CI checks for a branch ref, paginated and deduped by name.
    pub async fn list_checks_for_ref(
        &self,
        owner: &str,
        repo: &str,
        reference: &str,
    ) -> Result<Vec<CheckRun>> {
        #[derive(Deserialize)]
        struct CheckRunsResponse {
            total_count: usize,
            check_runs: Vec<CheckRun>,
        }

        let url = format!(

View on GitHub (pinned to 58e5313667)