tonhowtf/omniget · error · Error

refresh returned no audio format

Error message

refresh returned no audio format

What it means

The catch-all arm of the __typename match in extract_graphql_media. Any GraphQL tweet result whose __typename is not one of the handled variants (tombstone types, "Tweet", "TweetWithVisibilityResults") produces this generic "Post not available" error, meaning the response shape is unknown to the extractor.

Solutions

  1. Log the unhandled __typename value and add an explicit match arm for it
  2. Update the GraphQL query hash/operation ID to a version with known response shapes
  3. Verify you are passing tweet_result (the inner result object) rather than the outer wrapper node
  4. Check the library's repo/issues for reports of new Twitter __typename values

Example fix

// before
_ => Err(anyhow!("Post not available")),
// after
other => {
    tracing::warn!("[twitter] unhandled typename={:?} tweet_id={}", other, tweet_id);
    Err(anyhow!("Post not available: unhandled typename {:?}", other))
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Narrow the response shape before extraction
const t = result?.data?.tweetResult?.result;
if (!t || !['__typename' in t].every(Boolean)) throw new MalformedResponseError(id);

Type guard

function isKnownTypename(result) {
  return ['TweetTombstone','TweetUnavailable','Tweet','TweetWithVisibilityResults']
    .includes(result?.__typename);
}

Try / catch

match tweet.extract_graphql_media(id) {
    Err(e) if e.to_string() == "Post not available" => {
        tracing::warn!("unhandled tweet shape, tweet_id={}", id);
        mark_unavailable(id);
    }
    other => other,
}

Prevention

When it happens

Trigger: extract_graphql_media receives a tweet_result whose __typename is an unhandled value (e.g. a new Twitter entity type, a profile/redirection variant, or a missing __typename resolving to the wildcard arm).

Common situations: Twitter introduces a new __typename in its GraphQL responses; the extractor is fed the wrong JSON node (e.g. the outer tweet container instead of the inner result); API experiments rolling out new card/embed types to a fraction of responses.

Related errors


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

Appendix: source

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

    this.cancelYoutubeRefresh();
    const now = Math.floor(Date.now() / 1000);
    const delaySecs = Math.max(30, expiresAt - now - 60);
    this.youtubeRefreshTimer = setTimeout(() => {
      void this.refreshYoutubeStream(videoId);
    }, delaySecs * 1000);
  }

  private async refreshYoutubeStream(videoId: string) {
    if (!this.audio) return;
    if (this.youtubeCurrentVideoId !== videoId) return;
    if (this.currentTrack?.youtube_video_id !== videoId) return;
    try {
      const { studyYoutubePlayer } = await import("$lib/study-bridge");
      console.info("[study-music] yt refresh start", { videoId });
      const res = await studyYoutubePlayer({ videoId });
      const url = pickBestYoutubeAudio(res);
      const videoUrl = pickBestYoutubeVideo(res);
      if (!url) throw new Error("refresh returned no audio format");
      const wasPaused = this.audio.paused;
      const t = this.audio.currentTime;
      this.audio.crossOrigin = null;
      this.audio.src = url;
      this.audio.currentTime = t;
      this.youtubeVideoUrl = videoUrl;
      if (!wasPaused) await this.audio.play();
      this.youtubeChapters = res.chapters ?? this.youtubeChapters;
      this.youtubeRefreshFailureCount = 0;
      this.scheduleYoutubeRefresh(videoId, res.expires_at);
      console.info("[study-music] yt refresh ok", { videoId, expires_at: res.expires_at });
    } catch (err) {
      this.youtubeRefreshFailureCount += 1;
      console.warn(
        "[study-music] yt refresh failed",
        { videoId, attempt: this.youtubeRefreshFailureCount },
        err,
      );

View on GitHub (pinned to 8600b91f42)