ytdl-org/youtube-dl · error · ExtractorError
%s said: %s
Error message
%s said: %s
What it means
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.
Source
Thrown at youtube_dl/extractor/funimation.py:125
title = episode = title_data.get('title') or _search_kane('videoTitle') or self._og_search_title(webpage)
series = _search_kane('showName')
if series:
title = '%s - %s' % (series, title)
description = self._html_search_meta(['description', 'og:description'], webpage, fatal=True)
try:
headers = {}
if self._TOKEN:
headers['Authorization'] = 'Token %s' % self._TOKEN
sources = self._download_json(
'https://www.funimation.com/api/showexperience/%s/' % video_id,
video_id, headers=headers, query={
'pinst_id': ''.join([random.choice(string.digits + string.ascii_letters) for _ in range(8)]),
})['items']
except ExtractorError as e:
if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403:
error = self._parse_json(e.cause.read(), video_id)['errors'][0]
raise ExtractorError('%s said: %s' % (
self.IE_NAME, error.get('detail') or error.get('title')), expected=True)
raise
formats = []
for source in sources:
source_url = source.get('src')
if not source_url:
continue
source_type = source.get('videoType') or determine_ext(source_url)
if source_type == 'm3u8':
formats.extend(self._extract_m3u8_formats(
source_url, video_id, 'mp4',
m3u8_id='hls', fatal=False))
else:
formats.append({
'format_id': source_type,
'url': source_url,
})View on GitHub (pinned to 956b8c5855)
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
Example fix
# before youtube_dl 'https://www.funimation.com/en/shows/some-show/episode-1/' # ERROR: Funimation said: You are not subscribed to this series # after yt-dlp --cookies funi_cookies.txt 'https://www.funimation.com/en/shows/some-show/episode-1/'
Defensive patterns
Strategy: try-catch
Try / catch
try:
ydl.extract_info(url)
except ExtractorError as e:
if str(e).startswith('Funimation said:'):
detail = str(e).split('said:', 1)[1]
if 'region' in detail.lower() or 'geo' in detail.lower():
rerun_with_us_proxy(url)
else:
rerun_with_credentials(url) Prevention
- 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
When it happens
Trigger: _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.
Common situations: 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.
Related errors
- This video is only available via cable service provider subs
- ', '.join([e['message'] for e in entitlement_issues])
- error['description']
- error
- %s said: %s
AI-assisted analysis of ytdl-org/youtube-dl@956b8c5855 (2026-08-14).
Data as JSON: /api/errors/a13723810387ea76.
Report an issue: GitHub.