unslothai/unsloth · error · TranscriptUnavailable
YouTube returned a caption URL from an unexpected host.
Error message
YouTube returned a caption URL from an unexpected host.
What it means
TranscriptUnavailable raised by _validated_caption_url when a caption baseUrl (or a redirect target) is not an https URL whose hostname is in the allowlist _CAPTION_HOSTS. This is a defense so caption fetching only ever talks to trusted YouTube caption hosts, never to arbitrary hosts injected into player data.
Source
Thrown at studio/backend/core/youtube_transcript.py:234
def _default_track_index(tracks: list[dict[str, Any]], tracklist: dict[str, Any]) -> int:
"""Index of the caption track paired with the video's default audio track.
A multi-language video lists its tracks alphabetically, so track 0 is often an
unrelated translation rather than the language actually spoken.
"""
audio_tracks = tracklist.get("audioTracks") or []
audio_index = tracklist.get("defaultAudioTrackIndex")
if isinstance(audio_index, int) and 0 <= audio_index < len(audio_tracks):
caption_index = (audio_tracks[audio_index] or {}).get("defaultCaptionTrackIndex")
if isinstance(caption_index, int) and 0 <= caption_index < len(tracks):
return caption_index
return 0
def _validated_caption_url(url: str) -> SplitResult:
parsed = urlsplit(url)
if parsed.scheme != "https" or (parsed.hostname or "").lower() not in _CAPTION_HOSTS:
raise TranscriptUnavailable("YouTube returned a caption URL from an unexpected host.")
return parsed
def _caption_url(base_url: str) -> str:
"""Ask a caption baseUrl for json3, keeping the blank-valued params YouTube sends."""
parsed = _validated_caption_url(base_url)
query = parse_qs(parsed.query, keep_blank_values = True)
query["fmt"] = ["json3"]
return urlunsplit(parsed._replace(query = urlencode(query, doseq = True)))
async def _read_capped(response: httpx.Response, limit: int, message: str) -> bytes:
body = bytearray()
async for chunk in response.aiter_bytes():
body.extend(chunk)
if len(body) > limit:
raise TranscriptUnavailable(message)
return bytes(body)View on GitHub (pinned to 203007d190)
Solutions
- Update the _CAPTION_HOSTS allowlist in core/youtube_transcript.py with the new legitimate YouTube caption host (verify the host independently first).
- Verify nothing on the network path (proxy, TLS-terminating middlebox) rewrites the baseUrl.
- If the URL came from tampered data, discard that player response and re-fetch.
Example fix
# before
_CAPTION_HOSTS = {"www.youtube.com"} # new host youtube.googleapis.com rejected
# after
_CAPTION_HOSTS = {"www.youtube.com", "youtube.googleapis.com", "www.googlevideo.com"} Defensive patterns
Strategy: try-catch
Validate before calling
from urllib.parse import urlsplit
CAPTION_HOSTS = {"www.youtube.com"} # keep in sync with core/youtube_transcript.py
def is_trusted_caption_url(url: str) -> bool:
p = urlsplit(url)
return p.scheme == "https" and (p.hostname or "").lower() in CAPTION_HOSTS Type guard
def is_valid_caption_url(url: str) -> bool:
p = urlsplit(url)
return p.scheme == "https" and (p.hostname or "").lower() in _CAPTION_HOSTS Try / catch
try:
transcript = await fetch_transcript(video_id)
except TranscriptUnavailable as e:
if "unexpected host" in str(e):
logger.error("caption host allowlist may be stale: %s", e)
raise Prevention
- Keep the _CAPTION_HOSTS allowlist updated when YouTube rotates caption CDNs.
- Never disable the host check to 'make it work' — it exists to prevent SSRF via tampered player data.
- If a proxy rewrites URLs on your network, exempt YouTube caption traffic.
When it happens
Trigger: A caption track's baseUrl uses http instead of https, or points at a non-allowlisted host (e.g. a CDN change like a new googlevideo subdomain not in _CAPTION_HOSTS, or tampered/proxied player data); a redirect Location header leading off-host.
Common situations: YouTube rotating caption CDN hostnames after an update (allowlist goes stale); environments with DNS/proxy manipulation rewriting URLs.
Related errors
- load timed out (last progress: {prog})
- That is not a YouTube video link.
- YouTube will not play this video.
- This video has no captions.
- This video's captions are empty.
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/5061d407ba3ea87f.
Report an issue: GitHub.