sigoden/aichat · error · anyhow::Error

Not found branch or tag

Error message

Not found branch or tag

What it means

`crawl_gh_tree` first queries the GitHub API for a ref (`/repos/{owner}/{repo}` ref/branches/tags endpoint) to obtain the commit SHA. When the response JSON lacks `object.sha` — typically because the branch or tag does not exist or the ref name is wrong — it returns `anyhow!("Not found branch or tag")`. The request itself succeeded (HTTP-level errors are handled earlier), but the payload has no SHA.

Solutions

  1. Verify the branch/tag name in the URL exists in the repo (git ls-remote --heads/--tags origin).
  2. Use the default branch instead of a hardcoded one, or resolve `HEAD` first.
  3. Check the GitHub API response for an error message (rate limit, 404) — a 403/404 body also lacks object.sha.
  4. Ensure authentication if the repo or ref is private; unauthenticated requests can 404 on private refs.

Example fix

// before
let url = "https://raw.githubusercontent.com/owner/repo/master/docs"; // branch deleted
// after
let url = "https://raw.githubusercontent.com/owner/repo/main/docs"; // verify with git ls-remote --heads
Defensive patterns

Strategy: validation

Validate before calling

async fn ref_exists(owner: &str, repo: &str, r: &str, token: Option<&str>) -> bool {
    let url = format!("https://api.github.com/repos/{owner}/{repo}/git/ref/heads/{r}");
    let mut req = reqwest::Client::new().get(&url).header("User-Agent", "check");
    if let Some(t) = token { req = req.bearer_auth(t); }
    req.send().await.map(|v| v.status().is_success()).unwrap_or(false)
}

Try / catch

match crawl_gh_tree(owner, repo, r, client).await {
    Err(e) if e.to_string() == "Not found branch or tag" => {
        eprintln!("Branch/tag '{r}' not found; falling back to default branch");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Crawling a GitHub repo URL that specifies a branch or tag name which does not exist upstream, is misspelled, or was deleted; also when the API returns an error object instead of a ref object.

Common situations: Default branch renamed from `master` to `main` (or vice versa) in the URL; pinning a deleted tag/version; repo moved or renamed so the ref endpoint returns an error body without `object.sha`.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of sigoden/aichat@82976d349a (2026-09-09). Data as JSON: /api/errors/70e9bed6697d6a1b. Report an issue: GitHub.

Appendix: source

Thrown at src/utils/request.rs:333

    let repo = path_segs[2];
    let branch = path_segs[4];
    let root_path = path_segs[5..].join("/");

    let url = format!("https://api.github.com/repos/{owner}/{repo}/git/ref/heads/{branch}");

    let res_body: Value = client
        .get(&url)
        .header("User-Agent", USER_AGENT)
        .header("Accept", "application/vnd.github+json")
        .header("X-GitHub-Api-Version", "2022-11-28")
        .send()
        .await?
        .json()
        .await?;

    let sha = res_body["object"]["sha"]
        .as_str()
        .ok_or_else(|| anyhow!("Not found branch or tag"))?;

    let url = format!("https://api.github.com/repos/{owner}/{repo}/git/trees/{sha}?recursive=true");

    let res_body: Value = client
        .get(&url)
        .header("User-Agent", USER_AGENT)
        .header("Accept", "application/vnd.github+json")
        .header("X-GitHub-Api-Version", "2022-11-28")
        .send()
        .await?
        .json()
        .await?;
    let tree = res_body["tree"]
        .as_array()
        .ok_or_else(|| anyhow!("Invalid github repo tree"))?;
    let paths = tree
        .iter()
        .flat_map(|v| {

View on GitHub (pinned to 82976d349a)