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
- 查看错误消息中的 HTTP 码与 hint:403 配置 GITHUB_TOKEN 提升限额,404 核对 owner/name/分支,401 更换 token
- 用 curl -I 复现请求,确认是限流(X-RateLimit-Remaining: 0)还是路径错误
- 稍后重试(限流窗口重置或 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
- 配置 GITHUB_TOKEN 避免匿名限流
- 批量操作间加延时,避免短时间内打满限额
- 核对 owner/name/branch 拼写后再调用
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)