{"record":{"id":"0c15fb65dea4692e","repo":"ytdl-org/youtube-dl","slug":"episode-s-is-not-available-s","errorCode":null,"errorMessage":"Episode %s is not available: %s","messagePattern":"Episode (.+?) is not available: (.+?)","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"youtube_dl/extractor/bbc.py","lineNumber":509,"sourceCode":"            'http://www.bbc.co.uk/iplayer/playlist/%s' % playlist_id, playlist_id)\n\n    def _download_legacy_playlist_url(self, url, playlist_id=None):\n        return self._download_xml(\n            url, playlist_id, 'Downloading legacy playlist XML')\n\n    def _extract_from_legacy_playlist(self, playlist, playlist_id):\n        no_items = playlist.find('./{%s}noItems' % self._EMP_PLAYLIST_NS)\n        if no_items is not None:\n            reason = no_items.get('reason')\n            if reason == 'preAvailability':\n                msg = 'Episode %s is not yet available' % playlist_id\n            elif reason == 'postAvailability':\n                msg = 'Episode %s is no longer available' % playlist_id\n            elif reason == 'noMedia':\n                msg = 'Episode %s is not currently available' % playlist_id\n            else:\n                msg = 'Episode %s is not available: %s' % (playlist_id, reason)\n            raise ExtractorError(msg, expected=True)\n\n        for item in self._extract_items(playlist):\n            kind = item.get('kind')\n            if kind not in ('programme', 'radioProgramme'):\n                continue\n            title = playlist.find('./{%s}title' % self._EMP_PLAYLIST_NS).text\n            description_el = playlist.find('./{%s}summary' % self._EMP_PLAYLIST_NS)\n            description = description_el.text if description_el is not None else None\n\n            def get_programme_id(item):\n                def get_from_attributes(item):\n                    for p in ('identifier', 'group'):\n                        value = item.get(p)\n                        if value and re.match(r'^[pb][\\da-z]{7}$', value):\n                            return value\n                get_from_attributes(item)\n                mediator = item.find('./{%s}mediator' % self._EMP_PLAYLIST_NS)\n                if mediator is not None:","sourceCodeStart":491,"sourceCodeEnd":527,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/bbc.py#L491-L527","documentation":"Catch-all sibling in BBCCoUkIE._extract_from_legacy_playlist: the noItems element carries a reason attribute the extractor does not recognize (anything other than preAvailability/postAvailability/noMedia), so it raises 'Episode %s is not available: %s' with the raw reason string. Expected=True; the raw reason is passed through verbatim for diagnosis.","triggerScenarios":"BBC adds a new noItems reason value (e.g. a new rights or takedown code) that the if/elif chain does not cover; the legacy EMP backend returns an unusual or malformed reason for a specific programme.","commonSituations":"Site-side API evolution introducing new reason codes after this extractor version was written; geo/rights-specific reason codes only served to some regions; debugging sessions where you need the raw reason string to classify a new failure mode.","solutions":["Read the interpolated reason string — it is the BBC's own code and usually self-explanatory (rights, takedown, etc.).","Compare against the three known codes; if the new one is semantically equivalent to one of them (e.g. another geo code), add it to the if/elif chain in bbc.py:509 and submit upstream.","Reproduce by fetching the legacy playlist XML for that id directly and inspecting noItems.","Retry later — some transient backend states also surface as unknown reasons."],"exampleFix":"// before\nelif reason == 'noMedia':\n    msg = 'Episode %s is not currently available' % playlist_id\nelse:\n    msg = 'Episode %s is not available: %s' % (playlist_id, reason)\n\n// after: map newly observed geo-style codes onto the geo message while keeping the raw code visible\nelse:\n    if 'geo' in (reason or '').lower():\n        msg = 'Episode %s is only available in the UK (reason: %s)' % (playlist_id, reason)\n    else:\n        msg = 'Episode %s is not available: %s' % (playlist_id, reason)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    ydl.extract_info(episode_url)\nexcept ExtractorError as e:\n    m = re.search(r'is not available: (\\S+)', str(e))\n    if m:\n        classify_bbc_reason(m.group(1))  # log raw BBC reason; maybe a new geo/rights code\n    else:\n        raise","preventionTips":["Log the raw reason string; new BBC codes appearing here are an early signal of API change.","Watch extractor updates after BBC backend releases introduce new noItems reasons.","Classify unknown reasons as non-retryable until proven transient."],"tags":["bbc","availability","unknown-reason","legacy-playlist","api-change"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}