tonhowtf/omniget · error

No URL available

Error message

No URL available

What it means

After get_media_info succeeds, download picks the first entry of info.available_qualities as the canonical media URL. If that list is empty or the first entry has an empty url string, it fails with 'No URL available' because there is nothing to download.

Solutions

  1. Re-run get_media_info and check available_qualities is non-empty before calling download.
  2. Log/inspect the raw yt-dlp JSON (ytdlp::get_video_info result) to see whether the source actually exposes a media URL; if not, the post is likely private, deleted, or geo-blocked.
  3. Update yt-dlp to the latest version, since extractor fixes often restore missing URL extraction.
  4. Guard the caller: validate !info.available_qualities.is_empty() and first.url is a non-empty http(s) URL before download.

Example fix

// before
platform.download(opts).await?;
// after
anyhow::ensure!(!info.available_qualities.is_empty(), "no downloadable URL for {url}");
anyhow::ensure!(info.available_qualities[0].url.starts_with("http"), "quality entry has no URL");
platform.download(opts).await?;
Defensive patterns

Strategy: validation

Validate before calling

fn has_downloadable_url(info: &MediaInfo) -> bool {
    info.available_qualities
        .first()
        .map(|q| q.url.starts_with("http"))
        .unwrap_or(false)
}
// usage: anyhow::ensure!(has_downloadable_url(&info), "media info has no URL");

Type guard

fn first_quality(info: &MediaInfo) -> Option<&Quality> {
    info.available_qualities
        .first()
        .filter(|q| !q.url.is_empty() && q.url.starts_with("http"))
}

Try / catch

match platform.download(opts).await {
    Err(e) if e.to_string().contains("No URL available") => {
        let info = platform.get_media_info(url).await?; // re-extract
        if info.available_qualities.is_empty() {
            Err(anyhow!("post has no downloadable media (removed/private/geo-blocked?)"))
        } else {
            platform.download(opts).await
        }
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling download(opts) with a MediaInfo whose available_qualities is empty, or whose first quality entry has url == "".

Common situations: Douyin pages where extraction returned metadata but no direct media URL (region-locked or removed posts); a partially populated MediaInfo constructed by the caller instead of returned by get_media_info; upstream HTML/API layout changes that break URL extraction in the parser.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12). Data as JSON: /api/errors/6ef694e62d882532. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/omniget-core/src/platforms/douyin.rs:264

        opts: &DownloadOptions,
        progress: mpsc::Sender<ProgressUpdate>,
    ) -> anyhow::Result<DownloadResult> {
        let _ = progress.send(ProgressUpdate::percent(0.0)).await;

        let ytdlp_path = match &opts.ytdlp_path {
            Some(p) => p.clone(),
            None => ytdlp::find_ytdlp_cached()
                .await
                .ok_or_else(|| anyhow!("yt-dlp not found"))?,
        };

        let canonical = info
            .available_qualities
            .first()
            .map(|q| q.url.as_str())
            .unwrap_or("");
        if canonical.is_empty() {
            return Err(anyhow!("No URL available"));
        }
        let canonical = Self::resolve_url(canonical).await;

        let quality_height = opts
            .quality
            .as_ref()
            .and_then(|q| q.trim_end_matches('p').parse::<u32>().ok());

        let extra = Self::extra_flags();

        ytdlp::download_video(
            &ytdlp_path,
            &canonical,
            &opts.output_dir,
            quality_height,
            progress,
            opts.download_mode.as_deref(),
            opts.format_id.as_deref(),

View on GitHub (pinned to 8600b91f42)