{"record":{"id":"0d417a30f07272ca","repo":"gitbutlerapp/gitbutler","slug":"failed-to-get-repo","errorCode":null,"errorMessage":"Failed to get repo: {}","messagePattern":"Failed to get repo: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-github/src/client.rs","lineNumber":160,"sourceCode":"\n        let user: User = response.json().await?;\n\n        Ok(AuthenticatedUser {\n            login: user.login,\n            avatar_url: user.avatar_url,\n            name: user.name,\n            email: user.email,\n        })\n    }\n\n    /// Fetch repo-level metadata (currently just the caller's permissions\n    /// and whether the repo is a fork). Used by the desktop to decide\n    /// whether the authenticated user can merge a given PR.\n    pub async fn get_repo(&self, owner: &str, repo: &str) -> Result<GitHubRepository> {\n        let url = format!(\"{}/repos/{}/{}\", self.base_url, owner, repo);\n        let response = self.client.get(&url).send().await?;\n        if !response.status().is_success() {\n            bail!(\"Failed to get repo: {}\", response.status());\n        }\n        Ok(response.json().await?)\n    }\n\n    /// Fetch CI checks for a branch ref, paginated and deduped by name.\n    pub async fn list_checks_for_ref(\n        &self,\n        owner: &str,\n        repo: &str,\n        reference: &str,\n    ) -> Result<Vec<CheckRun>> {\n        #[derive(Deserialize)]\n        struct CheckRunsResponse {\n            total_count: usize,\n            check_runs: Vec<CheckRun>,\n        }\n\n        let url = format!(","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-github/src/client.rs#L142-L178","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify owner/repo with curl -H \"Authorization: Bearer $TOKEN\" https://api.github.com/repos/{owner}/{repo}","Check token scopes/permissions; regenerate with repo (or fine-grained repository read) access","Watch for 403 with rate-limit headers; wait for reset or use a token with higher limits"],"exampleFix":"// before\nlet repo = client.get_repo(\"acme\", \"reppo\").await?; // typo\n// after\nlet repo = client.get_repo(\"acme\", \"repo\").await?; // verified via API","handlingStrategy":"try-catch","validationCode":"let status = reqwest::Client::new()\n    .get(&format!(\"https://api.github.com/repos/{owner}/{repo}\"))\n    .header(\"Authorization\", format!(\"Bearer {token}\"))\n    .send().await?.status();\nif !status.is_success() { return Err(format!(\"repo unreachable: {status}\")); }","typeGuard":null,"tryCatchPattern":"match client.get_repo(owner, repo).await {\n    Ok(r) => r,\n    Err(e) if e.to_string().contains(\"Failed to get repo: 403\") => {\n        // rate-limited or forbidden: back off / check scopes\n        ...\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Verify owner/repo slugs before calling","Use a token with repository read access (repo scope or fine-grained read)","Watch rate-limit headers and back off on 403"],"tags":["github","api","http","repository"],"backgroundTag":"http-error-response","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}