tonhowtf/omniget · error

No video quality available

Error message

No video quality available

What it means

Raised by native_get_media_info after the clip metadata GraphQL query succeeds but the returned videoQualities array is empty, meaning Twitch returned the clip record with no playable quality renditions.

Solutions

  1. Fall back to the yt-dlp-based extraction path for the clip
  2. Re-check the clip in a browser to confirm it still exists and is public
  3. Add logging on the parsed videoQualities count to detect GQL schema drift

Example fix

// before
if clip.video_qualities.is_empty() {
    return Err(anyhow!("No video quality available"));
}
// after
if clip.video_qualities.is_empty() {
    return self.fallback_ytdlp(url).await;
}
Defensive patterns

Strategy: fallback

Validate before calling

// After get_media_info succeeds, before download:
if info.available_qualities.is_empty() {
    eprintln!("Twitch clip exposed no qualities; clip may be deleted or restricted");
}

Type guard

fn has_playable_qualities(info: &MediaInfo) -> bool {
    !info.available_qualities.is_empty() && info.available_qualities.iter().all(|q| !q.url.is_empty())
}

Try / catch

match downloader.get_media_info(url).await {
    Err(e) if e.to_string().contains("No video quality available") => fallback_ytdlp_info(url).await,
    other => other,
}

Prevention

When it happens

Trigger: fetch_clip_metadata returns a clip whose videoQualities array is empty — typical for deleted/expired clips, sub-only content, or clips in restricted regions; also when the GQL response fields are filtered out.

Common situations: A clip was deleted or made subscriber-only after the link was shared; geo-restriction strips renditions; Twitch API changes the videoQualities shape so all entries are filtered by filter_map.

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/e0d250af16abb0b0. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/omniget-core/src/platforms/twitch.rs:56

        Self::new()
    }
}

impl TwitchClipsDownloader {
    async fn fallback_ytdlp(&self, url: &str) -> anyhow::Result<MediaInfo> {
        let ytdlp_path = crate::core::ytdlp::ensure_ytdlp().await?;
        let json = crate::core::ytdlp::get_video_info(&ytdlp_path, url, &[]).await?;
        crate::platforms::generic_ytdlp::GenericYtdlpDownloader::parse_video_info(&json)
    }

    async fn native_get_media_info(&self, url: &str) -> anyhow::Result<MediaInfo> {
        let slug =
            Self::extract_clip_slug(url).ok_or_else(|| anyhow!("Could not extract clip slug"))?;

        let clip = self.fetch_clip_metadata(&slug).await?;

        if clip.video_qualities.is_empty() {
            return Err(anyhow!("No video quality available"));
        }

        let broadcaster = clip
            .broadcaster_login
            .as_deref()
            .ok_or_else(|| anyhow!("Dados do clip incompletos"))?;

        let token = self.fetch_access_token(&slug).await?;

        let clip_title = clip.title.trim().to_string();

        let available_qualities: Vec<VideoQuality> = clip
            .video_qualities
            .iter()
            .map(|q| {
                let height: u32 = q.quality.parse().unwrap_or(0);
                let authenticated_url = Self::build_authenticated_url(&q.source_url, &token);
                VideoQuality {

View on GitHub (pinned to 8600b91f42)