unslothai/unsloth · warning · TranscriptUnavailable

YouTube returned no caption text for this video.

Error message

YouTube returned no caption text for this video.

What it means

TranscriptUnavailable raised after the caption fetch completes when the response body is empty (falsy `body`). The request succeeded at HTTP level (raise_for_status passed) and redirects settled, but zero bytes of caption payload came back.

Source

Thrown at studio/backend/core/youtube_transcript.py:278

        # Redirects are followed by hand so the host allowlist covers every hop, not
        # just the URL the player response handed us.
        async with client.stream(
            "GET", url, headers = {"User-Agent": _USER_AGENT}, follow_redirects = False
        ) as response:
            location = response.headers.get("location")
            if response.is_redirect and location:
                url = urljoin(url, location)
                _validated_caption_url(url)
                continue
            response.raise_for_status()
            body = await _read_capped(
                response, _MAX_CAPTION_BYTES, "This video's captions are too large to attach."
            )
        break
    else:
        raise TranscriptUnavailable("YouTube redirected the caption request too many times.")
    if not body:
        raise TranscriptUnavailable("YouTube returned no caption text for this video.")

    try:
        payload = json.loads(body.decode("utf-8"))
    except (UnicodeDecodeError, ValueError) as error:
        raise TranscriptUnavailable("YouTube returned unreadable caption data.") from error
    events = payload.get("events") if isinstance(payload, dict) else None
    return _flatten_events(events or [])


def _flatten_events(events: list[Any]) -> str:
    lines: list[str] = []
    for event in events:
        if not isinstance(event, dict):
            continue
        # aAppend cues carry only the rolling-window newline between ASR lines.
        if event.get("aAppend") == 1:
            continue
        segments = event.get("segs")

View on GitHub (pinned to 203007d190)

Solutions

  1. Retry the fetch after a short backoff.
  2. Request a different track/language (the selected track may be a dead entry).
  3. Fall back to local transcription if YouTube keeps returning empty bodies.
Defensive patterns

Strategy: retry

Try / catch

try:
    transcript = await fetch_transcript(video_id)
except TranscriptUnavailable as e:
    if "no caption text" in str(e):
        transcript = await fetch_transcript(video_id, languages=("en",))  # other track, then give up
    else:
        raise

Prevention

When it happens

Trigger: Caption endpoint returning 200 with an empty body for a given fmt=json3 request; track exists but serves no data; transient server-side empty responses.

Common situations: Rare server-side glitches; requesting captions for tracks that were just disabled; rate-limited endpoints returning empty 200s.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/e8a375b7ef3b8a29. Report an issue: GitHub.