{"record":{"id":"570f8cd8b2a1c7bf","repo":"farion1231/cc-switch","slug":"error-570f8c","errorCode":null,"errorMessage":"所有分支下载失败","messagePattern":"所有分支下载失败","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"src-tauri/src/services/skill.rs","lineNumber":3119,"sourceCode":"                \"https://github.com/{}/{}/archive/refs/heads/{}.zip\",\n                repo.owner, repo.name, branch\n            );\n            Self::assert_github_archive_url(&url, &repo.owner, &repo.name)?;\n\n            match self.download_and_extract(&url, &temp_path).await {\n                Ok(_) => return Ok((temp_dir, branch.to_string())),\n                Err(e) => {\n                    // 每个分支各自重算预算，所以失败后必须把上一轮的残留清掉——\n                    // 否则 N 个候选分支等于 N 倍的落盘量堆在同一个目录里。\n                    let _ = fs::remove_dir_all(&temp_path);\n                    let _ = fs::create_dir_all(&temp_path);\n                    last_error = Some(e);\n                    continue;\n                }\n            }\n        }\n\n        Err(last_error.unwrap_or_else(|| anyhow::anyhow!(\"所有分支下载失败\")))\n    }\n\n    /// 下载并解压 ZIP\n    async fn download_and_extract(&self, url: &str, dest: &Path) -> Result<()> {\n        let client = crate::proxy::http_client::get();\n        let response = client.get(url).send().await?;\n        if !response.status().is_success() {\n            let status = response.status().as_u16().to_string();\n            return Err(anyhow::anyhow!(format_skill_error(\n                \"DOWNLOAD_FAILED\",\n                &[(\"status\", &status)],\n                match status.as_str() {\n                    \"403\" => Some(\"http403\"),\n                    \"404\" => Some(\"http404\"),\n                    \"429\" => Some(\"http429\"),\n                    _ => Some(\"checkNetwork\"),\n                },\n            )));","sourceCodeStart":3101,"sourceCodeEnd":3137,"githubUrl":"https://github.com/farion1231/cc-switch/blob/a2e22f330273a5b6ffa87cb8b82b624601bac562/src-tauri/src/services/skill.rs#L3101-L3137","documentation":"Fallback error at the bottom of download_repo: returned only when the candidate-branch loop makes zero attempts (branches list empty), because any real per-branch failure is captured in last_error and that concrete error is returned instead. With the current construction the list always contains at least 'main' and 'master', so this literal is effectively unreachable — callers normally see the last branch's DOWNLOAD_FAILED/timeout/parse error.","triggerScenarios":"Zero iterations of the loop over branches: currently impossible since 'main'/'master' are unconditionally pushed (even repo.branch == \"HEAD\" still yields [\"main\",\"master\"]). Would only surface if the candidate-construction logic later filters all entries out.","commonSituations":"Confusion in logs: developers grep for this message after a failed download, but the real cause is the per-branch error that replaced it via last_error.unwrap_or_else. Also surfaces after refactors that add branch filtering.","solutions":["Inspect the anyhow error chain (err.chain()/root cause) — the actual per-branch failure is what was returned, not this sentinel","If you genuinely hit this literal, debug the branches vec construction in download_repo (lines ~3087-3096) — something filtered every candidate","Improve the message to include the candidates so future hits are self-describing"],"exampleFix":"// before\nErr(last_error.unwrap_or_else(|| anyhow::anyhow!(\"所有分支下载失败\")))\n\n// after — make the sentinel self-describing if it ever fires\nErr(last_error.unwrap_or_else(|| {\n    anyhow::anyhow!(\"所有分支下载失败: candidates={branches:?}\")\n}))","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Rust — walk the anyhow chain; the real cause is a per-branch error, not the sentinel\nif let Err(e) = service.download_repo(&repo).await {\n    let root = e.chain().last().map(|c| c.to_string()).unwrap_or_default();\n    log::warn!(\"repo download failed, root cause: {root}\");\n    // branch on root: DOWNLOAD_FAILED/DOWNLOAD_TIMEOUT/ARCHIVE_* each need different UX\n}","preventionTips":["Log per-branch attempts and their errors instead of only the aggregate failure","When debugging a reported '所有分支下载失败', first check whether the string is the literal sentinel or a per-branch error that replaced it","Keep the candidate list construction ('main'/'master' fallback) intact in refactors — the sentinel only fires when it is emptied"],"tags":["download","github","unreachable-code","skill"],"backgroundTag":null,"analyzedSha":"a2e22f330273a5b6ffa87cb8b82b624601bac562","analyzedAt":"2026-08-16T03:46:07.889Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}