ytdl-org/youtube-dl · error · ExtractorError
%s said: %s
Error message
%s said: %s
What it means
SRGSSRIE._get_media_data reads the resolved chapter's 'blockReason' and maps it through the extractor's _ERRORS table. GEOBLOCK routes to raise_geo_restricted with the allowed countries; any other known reason is raised as '<SRF/RTS/...> said: <mapped message>' with expected=True. It is the platform's explicit content-blocking signal (end of availability, age rating, legal restrictions).
Source
Thrown at youtube_dl/extractor/srgssr.py:79
def _get_media_data(self, bu, media_type, media_id):
query = {'onlyChapters': True} if media_type == 'video' else {}
full_media_data = self._download_json(
'https://il.srgssr.ch/integrationlayer/2.0/%s/mediaComposition/%s/%s.json'
% (bu, media_type, media_id),
media_id, query=query)['chapterList']
try:
media_data = next(
x for x in full_media_data if x.get('id') == media_id)
except StopIteration:
raise ExtractorError('No media information found')
block_reason = media_data.get('blockReason')
if block_reason and block_reason in self._ERRORS:
message = self._ERRORS[block_reason]
if block_reason == 'GEOBLOCK':
self.raise_geo_restricted(
msg=message, countries=self._GEO_COUNTRIES)
raise ExtractorError(
'%s said: %s' % (self.IE_NAME, message), expected=True)
return media_data
def _real_extract(self, url):
bu, media_type, media_id = re.match(self._VALID_URL, url).groups()
media_data = self._get_media_data(bu, media_type, media_id)
title = media_data['title']
formats = []
q = qualities(['SD', 'HD'])
for source in (media_data.get('resourceList') or []):
format_url = source.get('url')
if not format_url:
continue
protocol = source.get('protocol')
quality = source.get('quality')
format_id = []View on GitHub (pinned to 956b8c5855)
Solutions
- Read the mapped message - 'end of availability' means the content expired permanently and cannot be fetched.
- If GEOBLOCK, use a CH/LI VPN or proxy; the allowed countries list is embedded in the extractor.
- Look for the same program on the broadcaster's official YouTube channel after the window.
- Update yt-dlp if blockReason keys were added/renamed upstream.
Defensive patterns
Strategy: try-catch
Validate before calling
chapter = get_media_data(bu, media_type, media_id)
if chapter.get('blockReason'):
reason = chapter['blockReason']
if reason == 'GEOBLOCK':
use_ch_proxy()
else:
skip(media_id, reason='blocked: ' + reason) Type guard
def chapter_is_blocked(chapter: dict) -> bool:
return bool(chapter.get('blockReason')) Try / catch
try:
extract(url)
except GeoRestrictedError:
retry_via_ch_proxy()
except ExtractorError as e:
if e.expected and 'said:' in str(e):
drop_expired(media_id, str(e))
else:
raise Prevention
- Check blockReason yourself before queuing SRGSSR downloads.
- Use CH/LI egress for geoblocked Swiss content.
- Download broadcast content within its availability window; expiry is permanent.
When it happens
Trigger: A mediaComposition chapter carrying blockReason equal to one of the extractor's known keys (e.g. 'ENDEAVOUR_END_AVAILABILITY' style 'end of availability' codes, geoblock, legal block). The chapter exists (so the StopIteration path passed) but the platform refuses playback.
Common situations: Swiss content expiring after its broadcast window (SRF/RTS take downs after a fixed availability period); viewers outside Switzerland/Liechtenstein hitting GEOBLOCK; sports content with shorter licenses than regular shows.
Related errors
AI-assisted analysis of ytdl-org/youtube-dl@956b8c5855 (2026-08-14).
Data as JSON: /api/errors/717b17857c56a227.
Report an issue: GitHub.