farion1231/cc-switch · error
EMPTY_ARCHIVE
EMPTY_ARCHIVE
Error message
EMPTY_ARCHIVE
What it means
Structured error from extract_repo_archive: the remote ZIP opened successfully but contains zero entries. Real GitHub branch archives always contain at least the repo-root directory entry, so an empty-but-valid ZIP means the downloaded bytes are not what the endpoint normally produces.
Source
Thrown at src-tauri/src/services/skill.rs:3253
)));
}
*total_bytes += amount;
Ok(())
}
/// 把 GitHub 仓库归档解压到 `dest`(剥掉归档自带的一层根目录)。
///
/// 与 `download_and_extract` 分离,使 zip-slip 防护可在不联网的情况下被单测覆盖。
fn extract_repo_archive<R: std::io::Read + std::io::Seek>(
mut archive: zip::ZipArchive<R>,
dest: &Path,
) -> Result<()> {
let root_name = if !archive.is_empty() {
let first_file = archive.by_index(0)?;
let name = first_file.name();
name.split('/').next().unwrap_or("").to_string()
} else {
return Err(anyhow::anyhow!(format_skill_error(
"EMPTY_ARCHIVE",
&[],
Some("checkRepoUrl"),
)));
};
// 归档字节完全由第三方控制(仓库可经 deeplink 添加),所以解压必须限量,
// 否则一个几 MB 的压缩炸弹就能塞满磁盘。webdav_sync/archive.rs 早有同款
// 双重上限,这条下载路径一直没有。
if archive.len() > MAX_ARCHIVE_ENTRIES {
let count = archive.len().to_string();
let limit = MAX_ARCHIVE_ENTRIES.to_string();
return Err(anyhow::anyhow!(format_skill_error(
"ARCHIVE_TOO_MANY_ENTRIES",
&[("count", &count), ("limit", &limit)],
Some("checkZipContent"),
)));
}View on GitHub (pinned to a2e22f3302)
Solutions
- Retry once — transient archive-service glitches do occur
- Try the repo's main branch explicitly (or clear the branch field) in case the named ref is in a broken state
- Verify https://github.com/{owner}/{name}/archive/refs/heads/{branch}.zip in a browser to see what is actually served
Defensive patterns
Strategy: validation
Validate before calling
// Rust — pre-flight: confirm the branch ref exists before discovery
async fn branch_exists(owner: &str, name: &str, branch: &str) -> Result<bool> {
let url = format!("https://api.github.com/repos/{owner}/{name}/branches/{branch}");
let resp = crate::proxy::http_client::get().get(url).send().await?;
Ok(resp.status().is_success())
} Type guard
export function isEmptyArchive(e: unknown): boolean {
return typeof e === "string" && e.includes('"code":"EMPTY_ARCHIVE"');
} Try / catch
match fetch_repo_skills(&repo).await {
Err(e) if e.to_string().contains("EMPTY_ARCHIVE") => {
// one retry with branch cleared (forces main/master), then give up with checkRepoUrl hint
}
other => other,
} Prevention
- An empty-but-valid ZIP almost always means a middlebox or archive-service anomaly — verify the archive URL in a browser
- Prefer the default branch when adding repos to minimize exotic-ref edge cases
When it happens
Trigger: The archive URL returned an empty (but structurally valid) ZIP — e.g. a proxy/mirror replacing the body, an edge case in GitHub's archive service, or a truncated download that still parses as an empty archive.
Common situations: Rare; usually paired with network middleboxes or a repo in a broken state. Distinct from a 404 (that fails earlier with DOWNLOAD_FAILED) and from a corrupt body (ZipArchive::new fails before this check).
Related errors
AI-assisted analysis of farion1231/cc-switch@a2e22f3302 (2026-08-16).
Data as JSON: /api/errors/17548cf6cddbf593.
Report an issue: GitHub.