unslothai/unsloth · error · TranscriptUnavailable

YouTube will not play this video.

Error message

YouTube will not play this video.

What it means

TranscriptUnavailable raised when the YouTube player response's playabilityStatus.status is neither None nor 'OK' and YouTube supplies no reason string. It means the player endpoint answered (HTTP-level success) but refused playback: the video is private, deleted, age/region restricted, or the client is being blocked from the innertube API.

Source

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

def watch_url(video_id: str) -> str:
    return f"https://www.youtube.com/watch?v={video_id}"


async def fetch_transcript(video_id: str, languages: Sequence[str] = ()) -> Transcript:
    """Download the captions for ``video_id``, preferring ``languages`` in order.

    Within a language a human-written track wins over an auto-generated one. With no
    match the track YouTube pairs with the video's default audio track is used.
    """
    if not _VIDEO_ID_RE.fullmatch(video_id):
        raise TranscriptUnavailable("That is not a YouTube video link.")

    async with httpx.AsyncClient(timeout = _TIMEOUT, follow_redirects = True) as client:
        player = await _fetch_player(client, video_id)
        status = (player.get("playabilityStatus") or {}).get("status")
        if status not in (None, "OK"):
            raise TranscriptUnavailable(
                (player.get("playabilityStatus") or {}).get("reason")
                or "YouTube will not play this video."
            )

        tracklist = (player.get("captions") or {}).get("playerCaptionsTracklistRenderer") or {}
        tracks = [t for t in (tracklist.get("captionTracks") or []) if t.get("baseUrl")]
        if not tracks:
            raise TranscriptUnavailable("This video has no captions.")

        track = _select_track(tracks, tracklist, languages)
        text = await _fetch_track_text(client, str(track["baseUrl"]))

    if not text:
        raise TranscriptUnavailable("This video's captions are empty.")
    text, truncated = _truncate_transcript(text)

    details = player.get("videoDetails") or {}
    return Transcript(

View on GitHub (pinned to 203007d190)

Solutions

  1. Verify the video plays in a browser with the same network egress; if it does not, the id is bad or restricted — remove it from your input set.
  2. Retry after a delay or from a different IP if you suspect bot-detection (status like LOGIN_REQUIRED); reduce request rate.
  3. Handle TranscriptUnavailable in the caller and skip/log the video rather than aborting the whole batch.

Example fix

# before
transcript = await fetch_transcript(video_id)  # raises, kills the batch task

# after
try:
    transcript = await fetch_transcript(video_id)
except TranscriptUnavailable as exc:
    logger.warning("skipping %s: %s", video_id, exc)
    transcript = None
Defensive patterns

Strategy: try-catch

Validate before calling

# Best-effort pre-check: HEAD the watch page and look for availability signals
import httpx

async def video_plays(video_id: str) -> bool:
    async with httpx.AsyncClient(follow_redirects=True) as client:
        r = await client.get(f"https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v={video_id}&format=json")
        return r.status_code == 200

Try / catch

try:
    transcript = await fetch_transcript(video_id)
except TranscriptUnavailable as e:
    logger.warning("unavailable %s: %s", video_id, e)
    continue  # skip video in batch flows

Prevention

When it happens

Trigger: Requesting transcripts for a private, deleted, or embed-disabled video; region- or age-restricted content; YouTube bot-detection returning LOGIN_REQUIRED/UNPLAYABLE statuses; any non-OK playabilityStatus where playabilityStatus.reason is absent so the fallback message is used.

Common situations: Bulk transcript fetching across stale/removed video ids; corporate network egress where YouTube serves a consent/bot page; newly uploaded videos still processing.

Related errors


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