{"record":{"id":"0144d910de351118","repo":"ytdl-org/youtube-dl","slug":"failed-to-find-episode-data","errorCode":null,"errorMessage":"Failed to find episode data","messagePattern":"Failed to find episode data","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"youtube_dl/extractor/callin.py","lineNumber":53,"sourceCode":"            'channel_url': 'https://callin.com/show/the-debrief-with-briahna-joy-gray-siiFDzGegm',\n        }\n    }]\n\n    def _search_nextjs_data(self, webpage, video_id, transform_source=None, fatal=True, **kw):\n        return self._parse_json(\n            self._search_regex(\n                r'(?s)<script[^>]+id=[\\'\"]__NEXT_DATA__[\\'\"][^>]*>([^<]+)</script>',\n                webpage, 'next.js data', fatal=fatal, **kw),\n            video_id, transform_source=transform_source, fatal=fatal)\n\n    def _real_extract(self, url):\n        video_id = self._match_id(url)\n        webpage = self._download_webpage(url, video_id)\n\n        next_data = self._search_nextjs_data(webpage, video_id)\n        episode = traverse_obj(next_data, ('props', 'pageProps', 'episode'), expected_type=dict)\n        if not episode:\n            raise ExtractorError('Failed to find episode data')\n\n        title = episode.get('title') or self._og_search_title(webpage)\n        description = episode.get('description') or self._og_search_description(webpage)\n\n        formats = []\n        formats.extend(self._extract_m3u8_formats(\n            episode.get('m3u8'), video_id, 'mp4',\n            entry_protocol='m3u8_native', fatal=False))\n        self._sort_formats(formats)\n\n        channel = try_get(episode, lambda x: x['show']['title'], compat_str)\n        channel_url = try_get(episode, lambda x: x['show']['linkObj']['resourceUrl'], compat_str)\n\n        return {\n            'id': video_id,\n            'title': title,\n            'description': description,\n            'formats': formats,","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/callin.py#L35-L71","documentation":"Raised by CallinIE when the parsed Next.js __NEXT_DATA__ JSON has no dict at props.pageProps.episode (traverse_obj returns None). The extractor relies entirely on that embedded structure to get title, m3u8 URL, and show metadata, so without it extraction cannot continue.","triggerScenarios":"Downloading a callin.com URL that is not an episode page (a show/landing/login page), a page rendered differently (SSR structure changed or data moved), or an episode that was removed so pageProps no longer contains 'episode'.","commonSituations":"Site redesign moving the episode payload to a different key or to client-side fetching; passing a stub or preview URL; episode unlisted/deleted.","solutions":["Confirm the URL is a real episode page (it should play in a plain browser).","Open the page source and search for __NEXT_DATA__; if 'episode' is absent, the data layout changed — switch to yt-dlp or a newer extractor.","If the show link redirects, follow the redirect and use the final URL.","Report the breakage / patch the traverse path to wherever the episode object now lives."],"exampleFix":"# example: make extraction resilient to a moved key\nepisode = (traverse_obj(next_data, ('props', 'pageProps', 'episode'), expected_type=dict)\n           or traverse_obj(next_data, ('props', 'pageProps', 'data', 'episode'), expected_type=dict))","handlingStrategy":"validation","validationCode":"import json, re\ndef has_episode_data(html):\n    m = re.search(r'<script[^>]+id=\"__NEXT_DATA__\"[^>]*>([^<]+)</script>', html)\n    if not m:\n        return False\n    data = json.loads(m.group(1))\n    return isinstance(data.get('props', {}).get('pageProps', {}).get('episode'), dict)","typeGuard":"def is_episode_page(next_data):\n    ep = next_data.get('props', {}).get('pageProps', {}).get('episode') if isinstance(next_data, dict) else None\n    return isinstance(ep, dict) and bool(ep.get('m3u8'))","tryCatchPattern":"from youtube_dl.utils import ExtractorError\ntry:\n    ydl.extract_info(url)\nexcept ExtractorError as e:\n    if 'Failed to find episode data' in str(e):\n        mark_as_not_an_episode(url)  # do not retry\n    else:\n        raise","preventionTips":["Validate the page contains a playable episode before queueing.","Keep episode URLs (not hub/landing URLs) in your job list.","Re-check page structure after site redesigns."],"tags":["callin","nextjs","extractor","missing-data"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}