{"record":{"id":"3ec936276d73bcd9","repo":"unslothai/unsloth","slug":"youtube-will-not-play-this-video","errorCode":null,"errorMessage":"YouTube will not play this video.","messagePattern":"YouTube will not play this video\\.","errorType":"exception","errorClass":"TranscriptUnavailable","httpStatus":null,"severity":"error","filePath":"studio/backend/core/youtube_transcript.py","lineNumber":116,"sourceCode":"\ndef watch_url(video_id: str) -> str:\n    return f\"https://www.youtube.com/watch?v={video_id}\"\n\n\nasync def fetch_transcript(video_id: str, languages: Sequence[str] = ()) -> Transcript:\n    \"\"\"Download the captions for ``video_id``, preferring ``languages`` in order.\n\n    Within a language a human-written track wins over an auto-generated one. With no\n    match the track YouTube pairs with the video's default audio track is used.\n    \"\"\"\n    if not _VIDEO_ID_RE.fullmatch(video_id):\n        raise TranscriptUnavailable(\"That is not a YouTube video link.\")\n\n    async with httpx.AsyncClient(timeout = _TIMEOUT, follow_redirects = True) as client:\n        player = await _fetch_player(client, video_id)\n        status = (player.get(\"playabilityStatus\") or {}).get(\"status\")\n        if status not in (None, \"OK\"):\n            raise TranscriptUnavailable(\n                (player.get(\"playabilityStatus\") or {}).get(\"reason\")\n                or \"YouTube will not play this video.\"\n            )\n\n        tracklist = (player.get(\"captions\") or {}).get(\"playerCaptionsTracklistRenderer\") or {}\n        tracks = [t for t in (tracklist.get(\"captionTracks\") or []) if t.get(\"baseUrl\")]\n        if not tracks:\n            raise TranscriptUnavailable(\"This video has no captions.\")\n\n        track = _select_track(tracks, tracklist, languages)\n        text = await _fetch_track_text(client, str(track[\"baseUrl\"]))\n\n    if not text:\n        raise TranscriptUnavailable(\"This video's captions are empty.\")\n    text, truncated = _truncate_transcript(text)\n\n    details = player.get(\"videoDetails\") or {}\n    return Transcript(","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/youtube_transcript.py#L98-L134","documentation":"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.","triggerScenarios":"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.","commonSituations":"Bulk transcript fetching across stale/removed video ids; corporate network egress where YouTube serves a consent/bot page; newly uploaded videos still processing.","solutions":["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.","Retry after a delay or from a different IP if you suspect bot-detection (status like LOGIN_REQUIRED); reduce request rate.","Handle TranscriptUnavailable in the caller and skip/log the video rather than aborting the whole batch."],"exampleFix":"# before\ntranscript = await fetch_transcript(video_id)  # raises, kills the batch task\n\n# after\ntry:\n    transcript = await fetch_transcript(video_id)\nexcept TranscriptUnavailable as exc:\n    logger.warning(\"skipping %s: %s\", video_id, exc)\n    transcript = None","handlingStrategy":"try-catch","validationCode":"# Best-effort pre-check: HEAD the watch page and look for availability signals\nimport httpx\n\nasync def video_plays(video_id: str) -> bool:\n    async with httpx.AsyncClient(follow_redirects=True) as client:\n        r = await client.get(f\"https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v={video_id}&format=json\")\n        return r.status_code == 200","typeGuard":null,"tryCatchPattern":"try:\n    transcript = await fetch_transcript(video_id)\nexcept TranscriptUnavailable as e:\n    logger.warning(\"unavailable %s: %s\", video_id, e)\n    continue  # skip video in batch flows","preventionTips":["Filter private/deleted ids from input lists before fetching.","Handle TranscriptUnavailable per-video so one bad id does not kill a batch.","Expect bot-detection on datacenter IPs; throttle and rotate egress if fetching at scale."],"tags":["youtube","transcript","network","playability"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}