{"record":{"id":"175999b0c2b1e17d","repo":"tonhowtf/omniget","slug":"no-url-available-175999","errorCode":null,"errorMessage":"No URL available","messagePattern":"No URL available","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/src/platforms/douyin/mod.rs","lineNumber":264,"sourceCode":"        opts: &DownloadOptions,\n        progress: mpsc::Sender<ProgressUpdate>,\n    ) -> anyhow::Result<DownloadResult> {\n        let _ = progress.send(ProgressUpdate::percent(0.0)).await;\n\n        let ytdlp_path = match &opts.ytdlp_path {\n            Some(p) => p.clone(),\n            None => ytdlp::find_ytdlp_cached()\n                .await\n                .ok_or_else(|| anyhow!(\"yt-dlp not found\"))?,\n        };\n\n        let canonical = info\n            .available_qualities\n            .first()\n            .map(|q| q.url.as_str())\n            .unwrap_or(\"\");\n        if canonical.is_empty() {\n            return Err(anyhow!(\"No URL available\"));\n        }\n        let canonical = Self::resolve_url(canonical).await;\n\n        let quality_height = opts\n            .quality\n            .as_ref()\n            .and_then(|q| q.trim_end_matches('p').parse::<u32>().ok());\n\n        let extra = Self::extra_flags();\n\n        ytdlp::download_video(\n            &ytdlp_path,\n            &canonical,\n            &opts.output_dir,\n            quality_height,\n            progress,\n            opts.download_mode.as_deref(),\n            opts.format_id.as_deref(),","sourceCodeStart":246,"sourceCodeEnd":282,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/src/platforms/douyin/mod.rs#L246-L282","documentation":"Douyin `download` picks the first entry of `available_qualities` as the canonical URL; if the list is empty or the URL is empty it aborts. It means the MediaInfo has no usable download URL even though download was invoked.","triggerScenarios":"Calling `download` with MediaInfo whose `available_qualities.first()` is None or whose first quality URL is an empty string.","commonSituations":"get_media_info partially failed (found title/qualities but no URL), a Douyin page whose video requires a refreshed resolution, or stale cached MediaInfo passed to a later download step.","solutions":["Re-run get_media_info to obtain fresh MediaInfo with populated URLs before downloading.","Check yt-dlp output for the Douyin link to confirm the platform actually exposes a direct media URL.","Validate in get_media_info that at least one non-empty URL exists and fail there with a clearer message.","Refresh cookies/auth for Douyin if extraction returns metadata but no media URLs."],"exampleFix":"// before\nlet canonical = info.available_qualities.first().map(|q| q.url.as_str()).unwrap_or(\"\");\nif canonical.is_empty() { return Err(anyhow!(\"No URL available\")); }\n// after\nlet canonical = info\n    .available_qualities\n    .iter()\n    .map(|q| q.url.as_str())\n    .find(|u| !u.is_empty())\n    .ok_or_else(|| anyhow!(\"No URL available for Douyin media '{}' (qualities empty)\", info.title))?;","handlingStrategy":"validation","validationCode":"// Rust, before calling download\nif info.available_qualities.iter().all(|q| q.url.is_empty()) {\n    return Err(anyhow!(\"Douyin MediaInfo has no usable URL; re-run get_media_info\"));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate MediaInfo immediately after get_media_info and before queuing the download.","Re-resolve fresh info rather than caching MediaInfo across long-running sessions.","Keep yt-dlp current so Douyin extraction keeps returning media URLs."],"tags":["downloader","douyin","empty-list","url"],"backgroundTag":"empty-required-field","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"}