tonhowtf/omniget · error · anyhow::Error

No video quality available

Error message

No video quality available

What it means

After fetching clip metadata from Twitch's GQL endpoint, native_get_media_info checks that the clip's video_qualities list is non-empty before building MediaInfo. This error means Twitch returned clip metadata but with no downloadable renditions, so there is nothing to download.

Solutions

  1. Retry fetch_clip_metadata — Twitch GQL responses are sometimes transiently incomplete.
  2. Verify the clip plays at clips.twitch.tv/<slug>; if deleted/banned, it cannot be downloaded.
  3. Fall back to the yt-dlp path, which can resolve clip sources differently.
  4. Check for Twitch API rate limiting or missing authentication headers in fetch_clip_metadata.

Example fix

// before
if clip.video_qualities.is_empty() {
    return Err(anyhow!("No video quality available"));
}
// after
if clip.video_qualities.is_empty() {
    return Err(anyhow!(
        "No video quality available for clip '{}' — it may be deleted or the channel banned",
        slug
    ));
}
Defensive patterns

Strategy: retry

Validate before calling

// After fetching metadata, check renditions before building MediaInfo
if clip.video_qualities.is_empty() {
    eprintln!("clip metadata has no renditions; retry or check clip online");
}

Type guard

fn has_renditions(clip: &ClipMetadata) -> bool {
    !clip.video_qualities.is_empty()
}

Try / catch

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

Prevention

When it happens

Trigger: get_media_info → native_get_media_info where fetch_clip_metadata(slug) succeeds but the clip's videoQualities array is empty — clips pending deletion, clips from banned/suspended channels, or Twitch API returning a degraded response.

Common situations: Very old clips whose renditions expired; clips from channels banned after posting; intermittent Twitch GQL responses missing videoQualities; rate-limited or unauthenticated GQL calls returning partial data.

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

Appendix: source

Thrown at src-tauri/src/platforms/twitch/mod.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)