tonhowtf/omniget · info · Error

empty stream url

Error message

empty stream url

What it means

Raised by extract_graphql_media when the tweet result uses the "Tweet" or "TweetWithVisibilityResults" typename but Self::media_arrays_from_tweet_result returns None — the tweet parsed successfully yet contains no recognizable media arrays (extended_entities/entities/media or similar).

Solutions

  1. Verify the tweet actually contains media before calling the extractor (or accept this as a normal skip result)
  2. Check for a recent Twitter GraphQL schema change and update media_arrays_from_tweet_result field paths
  3. Log the raw tweet_result JSON on failure to confirm which field names are present
  4. Use a pinned, known-good GraphQL operation ID/query hash in case the endpoint changed

Example fix

// before
let media = Self::media_arrays_from_tweet_result(tweet_result)
    .ok_or_else(|| anyhow!("No media found in tweet"))?;
// after
match Self::media_arrays_from_tweet_result(tweet_result) {
    Some(media) if !media.is_empty() => Ok(media),
    _ => Err(TwitterError::NoMedia { tweet_id }), // lets callers distinguish text-only tweets from schema breakage
}
Defensive patterns

Strategy: validation

Validate before calling

// Check the tweet has media before invoking media extraction
if (!tweet?.extended_entities?.media?.length && !tweet?.entities?.media?.length) {
  return skip(id, 'text-only tweet');
}

Type guard

function hasExtractableMedia(tweetResult) {
  return Array.isArray(tweetResult?.legacy?.extended_entities?.media) &&
         tweetResult.legacy.extended_entities.media.length > 0;
}

Try / catch

match tweet.extract_graphql_media(id) {
    Err(e) if e.to_string() == "No media found in tweet" => skip(id, Reason::TextOnly),
    other => other,
}

Prevention

When it happens

Trigger: Calling extract_graphql_media on a text-only tweet (no photos/videos/GIFs), or on a tweet whose media fields were stripped/renamed by a Twitter GraphQL schema change so media_arrays_from_tweet_result can no longer find them.

Common situations: Queuing arbitrary tweet URLs without checking they contain media; the account posted a poll/text-only reply; Twitter shipped a GraphQL schema change renaming entities fields, breaking extraction for ALL tweets until the parser is updated.

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

Appendix: source

Thrown at src/lib/study-music/player-store.svelte.ts:1565

    if (!this.audio) return;
    const videoId = track.youtube_video_id;
    if (!videoId) {
      this.setError(
        classifyPlayerError("video id ausente — track removed", "youtube", track.id),
      );
      return;
    }
    this.loading = true;
    await this.stopCurrentPlayback();
    this.youtubeCurrentVideoId = videoId;
    this.youtubeVideoUrl = null;
    this.youtubeChapters = [];
    this.youtubeSponsorBlockSegments = [];
    this.youtubeRefreshFailureCount = 0;
    try {
      const { audio: audioUrl, video: videoUrl } =
        await this.resolveYoutubeMedia(videoId);
      if (!audioUrl) throw new Error("empty stream url");
      this.audio.crossOrigin = null;
      this.audio.src = audioUrl;
      this.youtubeVideoUrl = videoUrl;
      void this.refreshYoutubeSponsorBlock(videoId);
      await this.audio.play();
      this.saveQueueNow();
      this.updateMediaSessionMetadata(track);
      void this.refreshDominantColor(track);
      void pluginInvoke("study", "study:music:youtube:track_record_play", {
        video_id: videoId,
      }).catch(() => {});
    } catch (e) {
      this.setError(classifyPlayerError(e, "youtube", track.id));
    }
  }

  private cancelYoutubeRefresh() {
    if (this.youtubeRefreshTimer) {

View on GitHub (pinned to 8600b91f42)