{"record":{"id":"3770f4989602684e","repo":"davila7/claude-code-templates","slug":"http","errorCode":null,"errorMessage":"HTTP {}","messagePattern":"HTTP \\{\\}","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"cli-rust/src/github.rs","lineNumber":90,"sourceCode":"    Ok(Some(files))\n}\n\nfn walk(\n    api_url: &str,\n    relative_path: &str,\n    skill_base_name: &str,\n    out: &mut Vec<DownloadedFile>,\n) -> Result<bool> {\n    let resp = client()?\n        .get(api_url)\n        .header(\"Accept\", \"application/vnd.github.v3+json\")\n        .send()?;\n\n    if resp.status().as_u16() == 404 {\n        return Ok(false);\n    }\n    if !resp.status().is_success() {\n        return Err(anyhow!(\"HTTP {}\", resp.status().as_u16()));\n    }\n\n    let contents: Value = resp.json()?;\n    let items = contents\n        .as_array()\n        .ok_or_else(|| anyhow!(\"unexpected contents API response\"))?;\n\n    for item in items {\n        let name = item.get(\"name\").and_then(|v| v.as_str()).unwrap_or(\"\");\n        let item_type = item.get(\"type\").and_then(|v| v.as_str()).unwrap_or(\"\");\n        let item_path = if relative_path.is_empty() {\n            name.to_string()\n        } else {\n            format!(\"{relative_path}/{name}\")\n        };\n\n        if item_type == \"file\" {\n            match item.get(\"download_url\").and_then(|v| v.as_str()) {","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/davila7/claude-code-templates/blob/a0851ed10c7c60463dac8cfaaca124cf32d5804d/cli-rust/src/github.rs#L72-L108","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Set a GITHUB_TOKEN (or gh auth token) to raise the rate limit from 60/hr to 5000/hr","Retry with backoff on 403/429 honoring Retry-After header","Confirm the repo/path still exists and is public (or token has access)","Cache directory listings to reduce contents API call volume during walk"],"exampleFix":"// before\nlet resp = client()?.get(url).send()?;\nif resp.status().as_u16() == 404 { return Ok(false); }\n// after — honor rate-limit Retry-After\nlet resp = client()?.get(url).send()?;\nlet status = resp.status().as_u16();\nif status == 404 { return Ok(false); }\nif status == 429 || status == 403 {\n    let wait = resp.headers().get(\"retry-after\").and_then(|v| v.to_str().ok()).and_then(|s| s.parse::<u64>().ok()).unwrap_or(60);\n    std::thread::sleep(Duration::from_secs(wait));\n    // retry once...\n}","handlingStrategy":"retry","validationCode":"// Check rate limit remaining before a walk burst\nlet remaining = client()?.get(\"https://api.github.com/rate_limit\").send()?\n    .json::<Value>()?[\"resources\"][\"core\"][\"remaining\"].as_u64().unwrap_or(0);\nif remaining < 50 { /* set GITHUB_TOKEN or delay */ }","typeGuard":null,"tryCatchPattern":"// match on the numeric status instead of blanket-propagating\nlet status = resp.status().as_u16();\nif status == 404 { return Ok(false); }\nif status == 429 || status == 403 {\n    let wait = resp.headers().get(\"retry-after\")\n        .and_then(|v| v.to_str().ok()).and_then(|s| s.parse::<u64>().ok()).unwrap_or(60);\n    std::thread::sleep(Duration::from_secs(wait));\n    // retry once, then give up\n}\nif !resp.status().is_success() { return Err(anyhow!(\"HTTP {status}\")); }","preventionTips":["Always configure GITHUB_TOKEN for CI or heavy use","Cache directory listings so repeat walks don't re-hit the contents API","Add jittered backoff around every GitHub API call"],"tags":["github-api","rate-limit","http-status","rust"],"backgroundTag":"github-api-rate-limited","analyzedSha":"a0851ed10c7c60463dac8cfaaca124cf32d5804d","analyzedAt":"2026-08-28T14:11:56.058Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}