{"record":{"id":"e56ce075fec39de9","repo":"farion1231/cc-switch","slug":"download-timeout","errorCode":"DOWNLOAD_TIMEOUT","errorMessage":"{\"code\":\"DOWNLOAD_TIMEOUT\",\"context\":{\"owner\":\"{owner}\",\"name\":\"{name}\",\"timeout\":\"60\"},\"suggestion\":\"checkNetwork\"}","messagePattern":"(.+?)\",\"name\":\"(.+?)\",\"timeout\":\"60\"\\},\"suggestion\":\"checkNetwork\"\\}","errorType":"error_code","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/src/services/skill.rs","lineNumber":827,"sourceCode":"        let mut downloaded_source: Option<(tempfile::TempDir, PathBuf)> = None;\n\n        // 如果已存在则跳过下载\n        if !dest.exists() {\n            let repo = SkillRepo {\n                owner: skill.repo_owner.clone(),\n                name: skill.repo_name.clone(),\n                branch: skill.repo_branch.clone(),\n                enabled: true,\n            };\n\n            // 下载仓库\n            let (temp_guard, used_branch) = timeout(\n                std::time::Duration::from_secs(60),\n                self.download_repo(&repo),\n            )\n            .await\n            .map_err(|_| {\n                anyhow!(format_skill_error(\n                    \"DOWNLOAD_TIMEOUT\",\n                    &[\n                        (\"owner\", &repo.owner),\n                        (\"name\", &repo.name),\n                        (\"timeout\", \"60\")\n                    ],\n                    Some(\"checkNetwork\"),\n                ))\n            })??;\n            let temp_dir = temp_guard.path();\n            repo_branch = used_branch;\n\n            // 复制到 SSOT\n            let source =\n                Self::resolve_skill_source_dir(temp_dir, &skill.directory).ok_or_else(|| {\n                    let missing = temp_dir.join(&source_rel).display().to_string();\n                    anyhow!(format_skill_error(\n                        \"SKILL_DIR_NOT_FOUND\",","sourceCodeStart":809,"sourceCodeEnd":845,"githubUrl":"https://github.com/farion1231/cc-switch/blob/a2e22f330273a5b6ffa87cb8b82b624601bac562/src-tauri/src/services/skill.rs#L809-L845","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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).","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"],"exampleFix":"// before (src-tauri/src/services/skill.rs)\nlet (temp_guard, used_branch) = timeout(std::time::Duration::from_secs(60), self.download_repo(&repo)).await\n\n// after\nlet (temp_guard, used_branch) = timeout(std::time::Duration::from_secs(180), self.download_repo(&repo)).await","handlingStrategy":"retry","validationCode":"// No local pre-check can predict a 60s timeout, but you can pre-flight reachability cheaply:\nawait fetch(\"https://github.com\", { mode: \"no-cors\", signal: AbortSignal.timeout(5000) });\n// if this throws, fix network/proxy before invoking install","typeGuard":null,"tryCatchPattern":"for (const delay of [0, 2000, 8000]) {\n  try {\n    await install(skill);\n    break;\n  } catch (e) {\n    if (isSkillError(e) && e.code === \"DOWNLOAD_TIMEOUT\" && delay < 8000) {\n      await new Promise((r) => setTimeout(r, delay));\n      continue;\n    }\n    throw e;\n  }\n}","preventionTips":["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"],"tags":["network","skills","timeout","github","download"],"backgroundTag":null,"analyzedSha":"a2e22f330273a5b6ffa87cb8b82b624601bac562","analyzedAt":"2026-08-16T03:46:07.889Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}