{"record":{"id":"97b0a578a66a7b2c","repo":"tonhowtf/omniget","slug":"all-playlist-item-s-failed-to-download-first-error","errorCode":null,"errorMessage":"All {} playlist item(s) failed to download. First error: {}","messagePattern":"All (.+?) playlist item\\(s\\) failed to download\\. First error: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/platforms/bilibili/legacy.rs","lineNumber":327,"sourceCode":"                success_count += 1;\n                last_result.file_size_bytes += result.file_size_bytes;\n                last_result.duration_seconds += result.duration_seconds;\n                last_result.file_path = result.file_path;\n            }\n            Err(e) => {\n                tracing::error!(\"[bilibili] playlist item {} failed: {}\", i + 1, e);\n                if first_error.is_none() {\n                    first_error = Some(e);\n                }\n            }\n        }\n    }\n\n    if success_count == 0 {\n        let detail = first_error\n            .map(|e| e.to_string())\n            .unwrap_or_else(|| \"unknown error\".to_string());\n        return Err(anyhow!(\n            \"All {} playlist item(s) failed to download. First error: {}\",\n            total,\n            detail\n        ));\n    }\n\n    let _ = progress.send(ProgressUpdate::percent(100.0)).await;\n    Ok(last_result)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn cheese_episode_is_not_playlist() {\n        assert!(!is_playlist_or_series(\n            \"https://www.bilibili.com/cheese/play/ep2143\"","sourceCodeStart":309,"sourceCodeEnd":345,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/platforms/bilibili/legacy.rs#L309-L345","documentation":"After iterating all playlist items, download_playlist() reports total failure if success_count == 0. It includes the first item's error as detail ('unknown error' if none was captured) so the caller learns why every item failed.","triggerScenarios":"download() -> download_playlist() where every playlist item's download future returned Err (network failures, expired CDN URLs, per-item extraction errors), leaving success_count at 0.","commonSituations":"Bilibili CDN URLs expired because fetching info and downloading were separated by too long; network/proxy outage; all items hit the same auth or region restriction; the per-item downloader has a systematic bug after an API change.","solutions":["Inspect 'First error: ...' in the message — fix the underlying per-item error it reports.","Re-fetch media info right before downloading so CDN URLs are fresh, then retry.","Check network/proxy connectivity and account cookies if the first error mentions auth or 403.","Improve the code to aggregate all item errors (not just the first) for better diagnostics."],"exampleFix":"// before\nreturn Err(anyhow!(\"All {} playlist item(s) failed to download. First error: {}\", total, detail));\n// after\nlet details: Vec<String> = item_errors.iter().map(|e| e.to_string()).take(3).collect();\nreturn Err(anyhow!(\"All {} playlist item(s) failed. First errors: {}\", total, details.join(\"; \")));","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match download(...).await {\n    Err(e) if e.to_string().starts_with(\"All \") && e.to_string().contains(\"playlist item(s) failed\") => {\n        let first = extract_first_error(&e.to_string());\n        retry_with_fresh_info(url).await\n    }\n    other => other,\n}","preventionTips":["Re-fetch media info immediately before downloading so CDN URLs are fresh.","Check the 'First error:' detail before retrying — fix the root cause.","Ensure stable network/proxy for long playlist downloads.","Aggregate per-item errors in your own logs for diagnosis."],"tags":["bilibili","playlist","batch-failure","download"],"backgroundTag":"api-request-failed","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}