BigPizzaV3/CodexPlusPlus · error · anyhow::Error

/ 的文件树请求失败:HTTP

Error message

{}/{} 的文件树请求失败:HTTP {}{}

What it means

fetch_repo_skills calls the GitHub trees API and, when the status is not a success, bails with the HTTP code plus github_status_hint context (403/429 rate limit, 404 repo/branch missing, 401 token issues). It fires whenever the tree request fails so the caller sees which repo failed and why.

Solutions

  1. 查看错误消息中的 HTTP 码与 hint:403 配置 GITHUB_TOKEN 提升限额,404 核对 owner/name/分支,401 更换 token
  2. 用 curl -I 复现请求,确认是限流(X-RateLimit-Remaining: 0)还是路径错误
  3. 稍后重试(限流窗口重置或 GitHub 故障恢复后)

Example fix

// before
let response = client.get(&url).send().await?;
// after
let response = client
    .get(&url)
    .header(reqwest::header::AUTHORIZATION, format!("Bearer {token}"))
    .send()
    .await?;
Defensive patterns

Strategy: retry

Validate before calling

// 调用前检查 token 是否已配置
let has_token = std::env::var("GITHUB_TOKEN").map(|t| !t.trim().is_empty()).unwrap_or(false);
if !has_token { eprintln!("警告:未配置 GITHUB_TOKEN,匿名限额易触发 403"); }

Try / catch

match fetch_repo_skills(&repo).await {
    Err(e) if e.to_string().contains("HTTP 403") => {
        eprintln!("GitHub 限流,等待限流窗口重置后重试");
        tokio::time::sleep(Duration::from_secs(60)).await;
        // 重试一次
    }
    Err(e) => eprintln!("拉取文件树失败:{e}"),
    Ok(skills) => { /* ... */ }
}

Prevention

When it happens

Trigger: reqwest GET 仓库文件树 API 返回 403(限流)、404(仓库/分支不存在)、401(token 无效)或任何非成功状态码时触发。

Common situations: 未配置 GitHub token 导致匿名请求触发 60 次/小时限流;仓库改名或删除;指定的分支/tag 不存在;token 过期或权限不足;GitHub 服务端 5xx。

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 BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19). Data as JSON: /api/errors/699d90e78226621b. Report an issue: GitHub.

Appendix: source

Thrown at crates/codex-plus-core/src/skills.rs:907

/// 拉一个仓库的 skill 清单:trees API 一次拿到全树,再按需补 SKILL.md 的标题和描述。
///
/// `cached` 传上一次的结果,哈希没变的 skill 直接复用元数据,不重复请求。
pub async fn fetch_repo_skills(
    repo: &SkillRepo,
    cached: &BTreeMap<String, RemoteSkill>,
) -> anyhow::Result<Vec<RemoteSkill>> {
    let client = github_client()?;
    let response = with_github_auth(client.get(repo_tree_url(repo)))
        .header(reqwest::header::ACCEPT, "application/vnd.github+json")
        .send()
        .await
        .with_context(|| format!("请求 {}/{} 的文件树失败", repo.owner, repo.name))?;
    // 不要用 error_for_status() 一把带过:403 限流、404 仓库/分支不存在、401 令牌
    // 无效,处理方式完全不同,但用户只会看到「返回错误状态」,没法自助也没法报障
    // (#1989 里四个仓库全失败,看不出到底是限流还是网络)。
    let status = response.status();
    if !status.is_success() {
        anyhow::bail!(
            "{}/{} 的文件树请求失败:HTTP {}{}",
            repo.owner,
            repo.name,
            status.as_u16(),
            github_status_hint(status.as_u16())
        );
    }
    let tree = response
        .json::<Value>()
        .await
        .context("解析 GitHub 文件树响应失败")?;

    let mut skills = parse_skills_from_tree(repo, &tree);
    for skill in &mut skills {
        if let Some(hit) = cached.get(&skill.id) {
            if hit.content_hash == skill.content_hash && !hit.description.is_empty() {
                skill.name = hit.name.clone();
                skill.description = hit.description.clone();

View on GitHub (pinned to b1ed92e5e4)