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

  1. Retry the install — transient stalls are the most common cause
  2. Check proxy settings (the client honors CC Switch's proxy configuration) and connectivity to github.com
  3. Install a smaller variant of the repo if the archive is hundreds of MB
  4. 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

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

Related errors


AI-assisted analysis of farion1231/cc-switch@a2e22f3302 (2026-08-16). Data as JSON: /api/errors/e56ce075fec39de9. Report an issue: GitHub.