farion1231/cc-switch · error · anyhow::Error
ARCHIVE_TOO_LARGE
ARCHIVE_TOO_LARGE
Error message
{"code":"ARCHIVE_TOO_LARGE","context":{"limit_mb":"{limit_mb}"},"suggestion":"checkZipContent"} What it means
Structured error from download_and_extract: the streamed response body exceeded MAX_ARCHIVE_DOWNLOAD_BYTES (128 MiB, skill.rs:327) while being read chunk-by-chunk into memory. This limit exists because the whole archive is buffered as a Vec<u8> before ZipArchive sees it — without the cap a multi-GB body would exhaust the heap before the extraction budget ever engages. Content-Length is deliberately not trusted; only bytes actually received are counted.
Source
Thrown at src-tauri/src/services/skill.rs:3148
&[("status", &status)],
match status.as_str() {
"403" => Some("http403"),
"404" => Some("http404"),
"429" => Some("http429"),
_ => Some("checkNetwork"),
},
)));
}
// 逐块读并卡住压缩体大小:`response.bytes()` 会先把攻击者控制的整个归档
// 收进内存,之后才轮到 ZipArchive 和解压预算——那时候堆已经被吃光了。
// 不能只信 Content-Length(可以撒谎或缺失),必须按实际收到的字节数算。
let mut response = response;
let mut body: Vec<u8> = Vec::new();
while let Some(chunk) = response.chunk().await? {
if body.len().saturating_add(chunk.len()) as u64 > MAX_ARCHIVE_DOWNLOAD_BYTES {
let limit_mb = (MAX_ARCHIVE_DOWNLOAD_BYTES / 1024 / 1024).to_string();
return Err(anyhow::anyhow!(format_skill_error(
"ARCHIVE_TOO_LARGE",
&[("limit_mb", &limit_mb)],
Some("checkZipContent"),
)));
}
body.extend_from_slice(&chunk);
}
let cursor = std::io::Cursor::new(body);
let archive = zip::ZipArchive::new(cursor)?;
Self::extract_repo_archive(archive, dest)
}
/// 按预算把单个归档条目写出,累计超限即中止。
///
/// 逐块累加而非读取归档头里声明的 size:那个值由归档作者填写,压缩炸弹会撒谎。
fn copy_entry_within_budget<R: std::io::Read, W: std::io::Write>(
reader: &mut R,View on GitHub (pinned to a2e22f3302)
Solutions
- Pick a leaner repo or a fork with assets stripped (git filter-repo / BFG) and add that instead
- Download the skill files manually and import them as a local ZIP — but note the local path enforces the same entry/byte budgets
- Only in a fork: raise MAX_ARCHIVE_DOWNLOAD_BYTES, understanding it raises peak memory usage of the app
Defensive patterns
Strategy: validation
Validate before calling
// Rust — pre-flight size check via the GitHub API (size is in KB)
async fn repo_too_big(owner: &str, name: &str) -> Result<bool> {
let url = format!("https://api.github.com/repos/{owner}/{name}");
let resp: serde_json::Value = crate::proxy::http_client::get().get(url).send().await?.json().await?;
Ok(resp["size"].as_u64().unwrap_or(0) > 128 * 1024) // conservative proxy for archive size
} Type guard
export function isArchiveTooLarge(e: unknown): boolean {
return typeof e === "string" && e.includes('"code":"ARCHIVE_TOO_LARGE"');
} Try / catch
match download_repo(&repo).await {
Err(e) if e.to_string().contains("ARCHIVE_TOO_LARGE") => {
// suggest a leaner repo or manual local import; do NOT retry — the size will not shrink
}
other => other,
} Prevention
- Check the repo's size on GitHub (Insights or API 'size' field) before adding large projects
- Keep skill repos markdown-only; strip binaries/media from history when curating
- Do not raise MAX_ARCHIVE_DOWNLOAD_BYTES casually — it bounds peak heap usage of the whole response body
When it happens
Trigger: Discovering/downloading a repo whose zip archive exceeds 128 MiB — typically repos bundling binaries, media, or vendored dependencies; or a malicious repo deliberately shipping a huge body (repos can be added via deeplink).
Common situations: Monorepos with assets; users expecting any GitHub repo to be installable; the doc comment notes skill repos are markdown, so 128 MiB is far beyond normal.
Related errors
- ARCHIVE_TOO_MANY_ENTRIES
- EMPTY_ARCHIVE
- Unsupported URL scheme
- DOWNLOAD_TIMEOUT
- Invalid skill directory (possible path traversal): {director
AI-assisted analysis of farion1231/cc-switch@a2e22f3302 (2026-08-16).
Data as JSON: /api/errors/14c268939ea5d642.
Report an issue: GitHub.