unslothai/unsloth · error · TranscriptUnavailable
YouTube returned an unreadable response.
Error message
YouTube returned an unreadable response.
What it means
TranscriptUnavailable raised by _fetch_player when the innertube player response body cannot be decoded as UTF-8 or parsed as JSON (UnicodeDecodeError or ValueError from json.loads, chained via `from error`). HTTP status was OK and the body was under the size cap, but the content is not a JSON document.
Source
Thrown at studio/backend/core/youtube_transcript.py:188
"osName": "Android",
"osVersion": "11",
"hl": "en",
"gl": "US",
},
},
"videoId": video_id,
"contentCheckOk": True,
"racyCheckOk": True,
},
) as response:
response.raise_for_status()
body = await _read_capped(
response, _MAX_PLAYER_BYTES, "YouTube returned an unexpectedly large response."
)
try:
player = json.loads(body.decode("utf-8"))
except (UnicodeDecodeError, ValueError) as error:
raise TranscriptUnavailable("YouTube returned an unreadable response.") from error
if not isinstance(player, dict):
raise TranscriptUnavailable("YouTube returned an unreadable response.")
return player
def _select_track(
tracks: list[dict[str, Any]], tracklist: dict[str, Any], languages: Sequence[str]
) -> dict[str, Any]:
for language in languages:
wanted = str(language).strip().lower()
if not wanted:
continue
base = wanted.split("-")[0]
for want_generated in (False, True):
candidates = [t for t in tracks if (t.get("kind") == "asr") is want_generated]
# exact locale before the base-language fallback: a pt-BR request must not
# take a pt-PT track just because it is listed first
for matches_wanted in (View on GitHub (pinned to 203007d190)
Solutions
- Retry the request after a delay or from a different network/IP.
- Inspect the raw response (log body[:200]) to identify consent/captcha pages; if consent-related, the request headers/client config need adjusting or cookies must be supplied.
- Rate-limit your fetching to avoid being served block pages.
Example fix
# before
player = await _fetch_player(client, video_id) # raises on HTML body
# after
for attempt in range(3):
try:
player = await _fetch_player(client, video_id)
break
except TranscriptUnavailable:
if attempt == 2:
raise
await asyncio.sleep(2 ** attempt) Defensive patterns
Strategy: retry
Try / catch
for attempt in range(3):
try:
player_or_transcript = await fetch_transcript(video_id)
break
except TranscriptUnavailable as e:
if "unreadable response" in str(e) and attempt < 2:
await asyncio.sleep(2 ** attempt)
continue
raise Prevention
- Rate-limit transcript requests to avoid HTML interstitial/bot pages.
- Log a body snippet when this fires to distinguish consent pages from corruption.
- Run fetchers from residential rather than datacenter egress when possible.
When it happens
Trigger: YouTube (or an intercepting proxy) returns an HTML consent page, captcha, or error page with HTTP 200; a truncated or gzip-mangled body; a captive portal injecting content. Any of these make json.loads fail and hit this branch.
Common situations: Running transcript fetches from datacenter IPs that get soft-blocked into HTML interstitials; corporate proxies rewriting responses; transient CDN corruption.
Related errors
- YouTube returned unreadable caption data.
- YouTube will not play this video.
- YouTube redirected the caption request too many times.
- load timed out (last progress: {prog})
- That is not a YouTube video link.
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/0947d6f00a0e869c.
Report an issue: GitHub.