{"record":{"id":"a13723810387ea76","repo":"ytdl-org/youtube-dl","slug":"s-said-s-a13723","errorCode":null,"errorMessage":"%s said: %s","messagePattern":"%s said: %s","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"youtube_dl/extractor/funimation.py","lineNumber":125,"sourceCode":"        title = episode = title_data.get('title') or _search_kane('videoTitle') or self._og_search_title(webpage)\n        series = _search_kane('showName')\n        if series:\n            title = '%s - %s' % (series, title)\n        description = self._html_search_meta(['description', 'og:description'], webpage, fatal=True)\n\n        try:\n            headers = {}\n            if self._TOKEN:\n                headers['Authorization'] = 'Token %s' % self._TOKEN\n            sources = self._download_json(\n                'https://www.funimation.com/api/showexperience/%s/' % video_id,\n                video_id, headers=headers, query={\n                    'pinst_id': ''.join([random.choice(string.digits + string.ascii_letters) for _ in range(8)]),\n                })['items']\n        except ExtractorError as e:\n            if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403:\n                error = self._parse_json(e.cause.read(), video_id)['errors'][0]\n                raise ExtractorError('%s said: %s' % (\n                    self.IE_NAME, error.get('detail') or error.get('title')), expected=True)\n            raise\n\n        formats = []\n        for source in sources:\n            source_url = source.get('src')\n            if not source_url:\n                continue\n            source_type = source.get('videoType') or determine_ext(source_url)\n            if source_type == 'm3u8':\n                formats.extend(self._extract_m3u8_formats(\n                    source_url, video_id, 'mp4',\n                    m3u8_id='hls', fatal=False))\n            else:\n                formats.append({\n                    'format_id': source_type,\n                    'url': source_url,\n                })","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/funimation.py#L107-L143","documentation":"Raised by the Funimation extractor when the showexperience API call returns 403 and the first entry of the body's 'errors' array is surfaced via its 'detail' or 'title' field. A 403 here means the session is not entitled to the stream: region lock, missing subscription, or an invalid/expired auth token in the Authorization header.","triggerScenarios":"_download_json of funimation.com/api/showexperience/<video_id>/?pinst_id=<random8> raises with an HTTPError 403; the body's errors[0] is parsed and raised as 'Funimation said: <detail|title>'. Occurs when self._TOKEN is absent (anonymous) or expired, the title is US-only while the requester is elsewhere, or the account lacks a premium subscription.","commonSituations":"Downloading without login from non-US IPs; tokens captured earlier expiring mid-session; free accounts hitting premium-only episodes; post-migration Funimation API behavior drift.","solutions":["Read the surfaced detail — it distinguishes geo-block from subscription errors","Authenticate: --username/--password (Funimation login) or --cookies from a logged-in premium session","For geo errors, use a US IP (Funimation streaming was region-segmented US/EU/UK with separate catalogs)","Since Funimation merged into Crunchyroll, use the Crunchyroll extractor with those credentials if the title migrated"],"exampleFix":"# before\nyoutube_dl 'https://www.funimation.com/en/shows/some-show/episode-1/'\n# ERROR: Funimation said: You are not subscribed to this series\n\n# after\nyt-dlp --cookies funi_cookies.txt 'https://www.funimation.com/en/shows/some-show/episode-1/'","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    ydl.extract_info(url)\nexcept ExtractorError as e:\n    if str(e).startswith('Funimation said:'):\n        detail = str(e).split('said:', 1)[1]\n        if 'region' in detail.lower() or 'geo' in detail.lower():\n            rerun_with_us_proxy(url)\n        else:\n            rerun_with_credentials(url)","preventionTips":["Always pass an authenticated session (cookies or token) for Funimation showexperience calls","Route through the correct region IP (US catalog vs EU/UK catalogs differed)","For migrated titles, use the Crunchyroll extractor instead"],"tags":["funimation","extractor","entitlement","forbidden"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}