unslothai/unsloth · error · TranscriptUnavailable

YouTube redirected the caption request too many times.

Error message

YouTube redirected the caption request too many times.

What it means

TranscriptUnavailable raised in _fetch_track_text when the manual redirect loop exhausts all iterations without getting a non-redirect response. The code follows redirects itself (validating each hop via _validated_caption_url) up to a fixed maximum; exceeding that budget triggers this instead of an infinite redirect loop.

Source

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

    body = b""
    for _ in range(_MAX_CAPTION_REDIRECTS + 1):
        # 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:

View on GitHub (pinned to 203007d190)

Solutions

  1. Retry once — transient redirect storms usually resolve.
  2. Ensure requests carry standard browser-like headers/cookies so YouTube does not loop through consent redirects.
  3. If persistent, capture the redirect chain (log each url) and report; the loop budget or cookie handling may need a fix in the client.
Defensive patterns

Strategy: retry

Try / catch

try:
    transcript = await fetch_transcript(video_id)
except TranscriptUnavailable as e:
    if "redirected the caption request too many times" in str(e):
        await asyncio.sleep(1.0)
        transcript = await fetch_transcript(video_id)
    else:
        raise

Prevention

When it happens

Trigger: YouTube's caption endpoint replying with a redirect chain longer than the loop budget (e.g. repeated 302 hops between consent and caption URLs); a misbehaving or adversarial server bouncing the request between allowed hosts indefinitely.

Common situations: Consent-cookie redirect loops when no consent cookies are sent; CDN misconfiguration loops; extremely rare in normal operation.

Related errors


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