farion1231/cc-switch · error · anyhow::Error

DOWNLOAD_TIMEOUT

DOWNLOAD_TIMEOUT

Error message

DOWNLOAD_TIMEOUT

What it means

install() wraps the repo download in tokio's timeout(std::time::Duration::from_secs(60), self.download_repo(&repo)) and maps the Elapsed error to DOWNLOAD_TIMEOUT, a structured JSON error (owner, name, timeout="60", suggestion 'checkNetwork'). This is a hard 60-second wall-clock budget covering the entire git clone/download of the skill repository — not a socket idle timeout — so slow networks, huge repos, or stalled connections all surface identically. The double '?!' after map_err means the inner download's own errors propagate separately; only exceeding 60s yields this code.

Solutions

  1. Check connectivity and retry once the network improves — transient stalls are the most common cause.
  2. Disable/adjust VPN or proxy for the git host, or switch to a faster network, then retry the install.
  3. If the repo is genuinely large, install from a smaller mirror or the author's trimmed skill-only repo; the fixed 60s budget cannot be raised from the client.
  4. If it reproduces consistently on fast networks, verify the repo URL/branch exists (a hanging redirect can mimic a timeout).

Example fix

# before: 300MB skills monorepo over hotel Wi-Fi
install(skill from "owner/huge-skills-monorepo")  # -> DOWNLOAD_TIMEOUT owner=owner name=huge-skills-monorepo timeout=60

# after: install from the trimmed single-skill repo
install(skill from "owner/pdf-tools")  # clone finishes well under 60s
Defensive patterns

Strategy: retry

Validate before calling

// Pre-flight reachability of the git host before install
let reachable = tokio::net::TcpStream::connect((repo_host.as_str(), 443)).await.is_ok();
if !reachable { return early_hint("checkNetwork"); }

Try / catch

let mut attempt = 0;
loop {
    match install(&db, &skill, &app).await {
        Err(e) if e.to_string().contains("DOWNLOAD_TIMEOUT") && attempt < 2 => { attempt += 1; tokio::time::sleep(Duration::from_secs(5)).await; continue; }
        other => break other?,
    }
}

Prevention

When it happens

Trigger: Installing a skill from a repository that takes more than 60s to clone: dial-up/high-latency links, a multi-hundred-MB repo, rate-limited or throttled git hosts, a proxy that stalls the transfer, or DNS/TLS handshake hanging inside download_repo.

Common situations: Corporate proxies/VPNs slowing GitHub; cloning during CI bandwidth contention; slow first clone of a monorepo that bundles assets; intermittent Wi-Fi.

Understand the failure class

Related errors


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

Appendix: source

Thrown at src-tauri/src/services/skill.rs:828

        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 06082e189d)