farion1231/cc-switch · error · anyhow::Error
DOWNLOAD_TIMEOUT
DOWNLOAD_TIMEOUT
Error message
{"code":"DOWNLOAD_TIMEOUT","context":{"owner":"{owner}","name":"{name}","timeout":"60"},"suggestion":"checkNetwork"} What it means
Structured network error from CC Switch's skill installer: install() wraps download_repo in tokio::time::timeout(Duration::from_secs(60)) (src-tauri/src/services/skill.rs:818-836). download_repo downloads and extracts the GitHub zip archive (trying the declared branch, then main, then master) via the shared proxy-aware HTTP client. If the whole future does not finish within 60 seconds, the Elapsed error is mapped to this JSON payload: code DOWNLOAD_TIMEOUT, context {owner, name, timeout: "60"}, suggestion 'checkNetwork'.
Source
Thrown at src-tauri/src/services/skill.rs:827
let mut downloaded_source: Option<(tempfile::TempDir, PathBuf)> = None;
// 如果已存在则跳过下载
if !dest.exists() {
let repo = SkillRepo {
owner: skill.repo_owner.clone(),
name: skill.repo_name.clone(),
branch: skill.repo_branch.clone(),
enabled: true,
};
// 下载仓库
let (temp_guard, used_branch) = timeout(
std::time::Duration::from_secs(60),
self.download_repo(&repo),
)
.await
.map_err(|_| {
anyhow!(format_skill_error(
"DOWNLOAD_TIMEOUT",
&[
("owner", &repo.owner),
("name", &repo.name),
("timeout", "60")
],
Some("checkNetwork"),
))
})??;
let temp_dir = temp_guard.path();
repo_branch = used_branch;
// 复制到 SSOT
let source =
Self::resolve_skill_source_dir(temp_dir, &skill.directory).ok_or_else(|| {
let missing = temp_dir.join(&source_rel).display().to_string();
anyhow!(format_skill_error(
"SKILL_DIR_NOT_FOUND",View on GitHub (pinned to a2e22f3302)
Solutions
- Retry the install — transient stalls are the most common cause
- Check proxy settings (the client honors CC Switch's proxy configuration) and connectivity to github.com
- Install a smaller variant of the repo if the archive is hundreds of MB
- If you build the app, raise the Duration in install() for your environment
Example fix
// before (src-tauri/src/services/skill.rs) let (temp_guard, used_branch) = timeout(std::time::Duration::from_secs(60), self.download_repo(&repo)).await // after let (temp_guard, used_branch) = timeout(std::time::Duration::from_secs(180), self.download_repo(&repo)).await
Defensive patterns
Strategy: retry
Validate before calling
// No local pre-check can predict a 60s timeout, but you can pre-flight reachability cheaply:
await fetch("https://github.com", { mode: "no-cors", signal: AbortSignal.timeout(5000) });
// if this throws, fix network/proxy before invoking install Try / catch
for (const delay of [0, 2000, 8000]) {
try {
await install(skill);
break;
} catch (e) {
if (isSkillError(e) && e.code === "DOWNLOAD_TIMEOUT" && delay < 8000) {
await new Promise((r) => setTimeout(r, delay));
continue;
}
throw e;
}
} Prevention
- Configure the proxy in CC Switch so the shared HTTP client can reach github.com
- Prefer lean skill repos; the 60s budget covers download plus zip extraction of every candidate branch
- Surface the structured timeout context (owner/name/60) to users with a retry affordance
When it happens
Trigger: Slow or stalled connection while cloning a large skill repo (the timeout covers download AND zip extraction); proxy misconfiguration making the GitHub archive URL hang; constrained networks where codeload.github.com is throttled.
Common situations: Corporate proxies/VPNs; first install of a monorepo with a huge archive; mobile/hotspot links; GitHub rate-limiting or transient slowdowns (though 429 has its own DOWNLOAD_FAILED mapping).
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
AI-assisted analysis of farion1231/cc-switch@a2e22f3302 (2026-08-16).
Data as JSON: /api/errors/e56ce075fec39de9.
Report an issue: GitHub.