{"record":{"id":"86646b0c8020e8ec","repo":"unslothai/unsloth","slug":"that-is-not-a-youtube-video-link","errorCode":null,"errorMessage":"That is not a YouTube video link.","messagePattern":"That is not a YouTube video link\\.","errorType":"exception","errorClass":"TranscriptUnavailable","httpStatus":null,"severity":"error","filePath":"studio/backend/core/youtube_transcript.py","lineNumber":110,"sourceCode":"        for prefix in _ID_PATH_PREFIXES:\n            if parsed.path.startswith(prefix):\n                candidate = parsed.path[len(prefix) :].split(\"/\", 1)[0]\n                break\n    return candidate if _VIDEO_ID_RE.fullmatch(candidate) else None\n\n\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","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/youtube_transcript.py#L92-L128","documentation":"TranscriptUnavailable raised by fetch_transcript when the video_id argument does not fullmatch the YouTube video id regex (_VIDEO_ID_RE, the 11-character [-A-Za-z0-9_] id format). The function takes a bare video id, not a URL, so passing a full watch URL or a malformed id fails this check before any network call.","triggerScenarios":"Calling fetch_transcript with a full URL such as 'https://www.youtube.com/watch?v=abc' instead of the 11-char id; passing a youtu.be short link; passing an extracted id that includes whitespace, query params, or is the wrong length/charset.","commonSituations":"Caller forgets to run the id through an extractor/regex before calling; ids copied with trailing newline or spaces; shorts URLs parsed incorrectly.","solutions":["Extract the 11-character video id before calling (e.g. re.search(r'(?:v=|youtu\\.be/|/shorts/)([A-Za-z0-9_-]{11})', url)).","Strip whitespace from the id.","If you control upstream, validate the id with the same pattern before dispatching the request."],"exampleFix":"# before\ntranscript = await fetch_transcript(\"https://www.youtube.com/watch?v=dQw4w9WgXcQ\")\n\n# after\nimport re\nmatch = re.search(r\"(?:v=|youtu\\.be/|/shorts/)([A-Za-z0-9_-]{11})\", url)\ntranscript = await fetch_transcript(match.group(1)) if match else None","handlingStrategy":"validation","validationCode":"import re\n\n_VIDEO_ID_RE = re.compile(r\"^[A-Za-z0-9_-]{11}$\")\n\ndef extract_video_id(url_or_id: str) -> str | None:\n    s = url_or_id.strip()\n    if _VIDEO_ID_RE.fullmatch(s):\n        return s\n    m = re.search(r\"(?:v=|youtu\\.be/|/shorts/|/embed/)([A-Za-z0-9_-]{11})\", s)\n    return m.group(1) if m else None\n\nvideo_id = extract_video_id(user_input)\nif video_id is None:\n    return \"Please provide a valid YouTube link or 11-character video id.\"","typeGuard":"def is_youtube_video_id(value: str) -> bool:\n    import re\n    return bool(re.fullmatch(r\"[A-Za-z0-9_-]{11}\", value.strip()))","tryCatchPattern":"try:\n    transcript = await fetch_transcript(video_id)\nexcept TranscriptUnavailable as e:\n    if \"not a YouTube video link\" in str(e):\n        return bad_request_error(str(e))\n    raise","preventionTips":["Always extract the 11-char id from URLs before calling fetch_transcript.","Strip whitespace/newlines from user-supplied ids.","Validate ids client-side with the same regex pattern."],"tags":["youtube","transcript","input-validation","regex"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}