{"record":{"id":"725dde1a6e8d78e9","repo":"tonhowtf/omniget","slug":"no-url-available-725dde","errorCode":null,"errorMessage":"No URL available","messagePattern":"No URL available","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/src/platforms/direct_file/mod.rs","lineNumber":104,"sourceCode":"            media_type: MediaType::File,\n            file_size_bytes,\n        })\n    }\n\n    async fn download(\n        &self,\n        info: &MediaInfo,\n        opts: &DownloadOptions,\n        progress: mpsc::Sender<ProgressUpdate>,\n    ) -> anyhow::Result<DownloadResult> {\n        let _ = progress.send(ProgressUpdate::percent(0.0)).await;\n\n        let file_url = info\n            .available_qualities\n            .first()\n            .map(|q| q.url.as_str())\n            .filter(|u| !u.is_empty())\n            .ok_or_else(|| anyhow!(\"No URL available\"))?;\n\n        let filename = sanitize_filename::sanitize(&info.title);\n        let filename = if filename.is_empty() {\n            filename_from_url(file_url)\n        } else {\n            filename\n        };\n        let output_path = opts.output_dir.join(&filename);\n\n        let mut builder = http_client::apply_global_proxy(reqwest::Client::builder())\n            .connect_timeout(std::time::Duration::from_secs(30));\n\n        if let Some(ua) = opts.user_agent.as_deref() {\n            builder = builder.user_agent(ua);\n        }\n\n        let jar = crate::core::cookie_parser::load_extension_cookies_for_url(file_url).or_else(|| {\n            opts.referer","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/src/platforms/direct_file/mod.rs#L86-L122","documentation":"In direct-file download, the code takes the first entry of `available_qualities`, extracts its URL, and rejects empty strings before downloading. It means the MediaInfo carried no usable direct URL for the file.","triggerScenarios":"`download` called with MediaInfo whose `available_qualities` is empty, or whose first quality has an empty `url` string.","commonSituations":"Upstream resolution produced an info record but failed to fill the URL (expired link, failed extraction), or the first quality entry was constructed as a placeholder with an empty URL.","solutions":["Inspect the MediaInfo before download and ensure the URL extraction step actually populated a URL.","Validate resolved URLs (non-empty, absolute) in get_media_info and fail early there with a clearer error.","Re-resolve the source URL if the original link expired and retry to get a fresh direct URL.","Fall back to deriving the filename and URL from the original user-supplied link when qualities are empty."],"exampleFix":"// before\nlet file_url = info.available_qualities.first().map(|q| q.url.as_str()).filter(|u| !u.is_empty()).ok_or_else(|| anyhow!(\"No URL available\"))?;\n// after\nlet file_url = 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 in qualities ({} entries) for {}\", info.available_qualities.len(), info.title))?;","handlingStrategy":"validation","validationCode":"// Rust, before calling download\nlet has_url = info\n    .available_qualities\n    .iter()\n    .any(|q| !q.url.is_empty());\nif !has_url {\n    return Err(anyhow!(\"Direct file info has no usable URL\"));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Fail early in get_media_info if no non-empty URL was extracted.","Validate URLs (non-empty, absolute, http/https) right after resolution.","Re-resolve expired direct links instead of reusing stale MediaInfo."],"tags":["downloader","direct-file","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"}