davila7/claude-code-templates · error · anyhow::Error

HTTP {}

Error message

HTTP {}

What it means

The GitHub contents API request inside `walk` returned a non-success, non-404 HTTP status. The code explicitly maps 404 to Ok(false) but any other status (401, 403, 429, 5xx) becomes an anyhow error showing the numeric code.

Source

Thrown at cli-rust/src/github.rs:90

    Ok(Some(files))
}

fn walk(
    api_url: &str,
    relative_path: &str,
    skill_base_name: &str,
    out: &mut Vec<DownloadedFile>,
) -> Result<bool> {
    let resp = client()?
        .get(api_url)
        .header("Accept", "application/vnd.github.v3+json")
        .send()?;

    if resp.status().as_u16() == 404 {
        return Ok(false);
    }
    if !resp.status().is_success() {
        return Err(anyhow!("HTTP {}", resp.status().as_u16()));
    }

    let contents: Value = resp.json()?;
    let items = contents
        .as_array()
        .ok_or_else(|| anyhow!("unexpected contents API response"))?;

    for item in items {
        let name = item.get("name").and_then(|v| v.as_str()).unwrap_or("");
        let item_type = item.get("type").and_then(|v| v.as_str()).unwrap_or("");
        let item_path = if relative_path.is_empty() {
            name.to_string()
        } else {
            format!("{relative_path}/{name}")
        };

        if item_type == "file" {
            match item.get("download_url").and_then(|v| v.as_str()) {

View on GitHub (pinned to a0851ed10c)

Solutions

  1. Set a GITHUB_TOKEN (or gh auth token) to raise the rate limit from 60/hr to 5000/hr
  2. Retry with backoff on 403/429 honoring Retry-After header
  3. Confirm the repo/path still exists and is public (or token has access)
  4. Cache directory listings to reduce contents API call volume during walk

Example fix

// before
let resp = client()?.get(url).send()?;
if resp.status().as_u16() == 404 { return Ok(false); }
// after — honor rate-limit Retry-After
let resp = client()?.get(url).send()?;
let status = resp.status().as_u16();
if status == 404 { return Ok(false); }
if status == 429 || status == 403 {
    let wait = resp.headers().get("retry-after").and_then(|v| v.to_str().ok()).and_then(|s| s.parse::<u64>().ok()).unwrap_or(60);
    std::thread::sleep(Duration::from_secs(wait));
    // retry once...
}
Defensive patterns

Strategy: retry

Validate before calling

// Check rate limit remaining before a walk burst
let remaining = client()?.get("https://api.github.com/rate_limit").send()?
    .json::<Value>()?["resources"]["core"]["remaining"].as_u64().unwrap_or(0);
if remaining < 50 { /* set GITHUB_TOKEN or delay */ }

Try / catch

// match on the numeric status instead of blanket-propagating
let status = resp.status().as_u16();
if status == 404 { return Ok(false); }
if status == 429 || status == 403 {
    let wait = resp.headers().get("retry-after")
        .and_then(|v| v.to_str().ok()).and_then(|s| s.parse::<u64>().ok()).unwrap_or(60);
    std::thread::sleep(Duration::from_secs(wait));
    // retry once, then give up
}
if !resp.status().is_success() { return Err(anyhow!("HTTP {status}")); }

Prevention

When it happens

Trigger: Walking a skill tree via download_skill_tree when GitHub rate limits the unauthenticated API (403/429 with no token), an expired/revoked token is used (401), the repo was made private or removed mid-walk, or GitHub returns 5xx.

Common situations: CI pipelines hitting the 60 req/hr unauthenticated rate limit; missing GITHUB_TOKEN env var; repo renamed/deleted; GitHub secondary rate limiting on many rapid contents API calls.

Related errors


AI-assisted analysis of davila7/claude-code-templates@a0851ed10c (2026-08-28). Data as JSON: /api/errors/3770f4989602684e. Report an issue: GitHub.