ytdl-org/youtube-dl · error · ExtractorError
YouTube said: %s
Error message
YouTube said: %s
What it means
Raised by _extract_and_report_alerts when the YouTube response's alerts array contains an alert of type 'error' (and fatal=True): the last error alert's message becomes 'YouTube said: <message>'. Non-error alerts and 'Unavailable videos will be hidden during playback' are only warned. expected comes from the caller, so playlist-level extraction usually treats it as an expected, user-visible condition.
Source
Thrown at youtube_dl/extractor/youtube.py:4038
continue
message = self._get_text(alert_dict, 'text')
if message:
yield alert_type, message
errors, warnings = [], []
_IGNORED_WARNINGS = T('Unavailable videos will be hidden during playback')
for alert_type, alert_message in alerts():
if alert_type.lower() == 'error' and fatal:
errors.append([alert_type, alert_message])
elif alert_message not in _IGNORED_WARNINGS:
warnings.append([alert_type, alert_message])
for alert_type, alert_message in itertools.chain(warnings, errors[:-1]):
self.report_warning(
'YouTube said: %s - %s' % (alert_type, alert_message),
only_once=only_once)
if errors:
raise ExtractorError(
'YouTube said: %s' % (errors[-1][1],), expected=expected)
def _extract_from_tabs(self, item_id, webpage, data, tabs):
selected_tab = self._extract_selected_tab(tabs)
renderer = traverse_obj(data, ('metadata', 'channelMetadataRenderer'),
expected_type=dict) or {}
playlist_id = item_id
title = description = None
if renderer:
channel_title = txt_or_none(renderer.get('title')) or item_id
tab_title = txt_or_none(selected_tab.get('title'))
title = join_nonempty(
channel_title or item_id, tab_title,
txt_or_none(selected_tab.get('expandedText')),
delim=' - ')
description = txt_or_none(renderer.get('description'))
playlist_id = txt_or_none(renderer.get('externalId')) or playlist_id
else:View on GitHub (pinned to 956b8c5855)
Solutions
- Act on the alert message text — it is YouTube's own statement about the playlist/channel item (removed, private, nonexistent).
- Verify the URL opens in a browser; nonexistent or private playlists legitimately error.
- Update youtube-dl if alert handling or the surrounding tab extraction regressed.
- For batch crawling, catch this per-playlist and continue with other items.
Defensive patterns
Strategy: try-catch
Validate before calling
def has_error_alerts(data: dict) -> bool:
return any(a.get('alertRenderer', {}).get('type', '').lower() == 'error'
for a in (data.get('alerts') or [])) Try / catch
try:
entries = ydl.extract_info(playlist_url)
except ExtractorError as e:
if str(e).startswith('YouTube said:'):
skip_playlist(playlist_url) # upstream error alert; read message text
else:
raise Prevention
- Check that playlist/channel URLs resolve in a browser before batch jobs.
- Log the alert message verbatim — it identifies private/deleted/nonexistent items deterministically.
When it happens
Trigger: Any channel/playlist/mix extraction whose API response includes an error alert — e.g. 'This video is unavailable', 'The playlist does not exist', 'This video is private' inside playlist continuation payloads.
Common situations: Dead/private playlist or channel URLs; playlists where YouTube injects error alerts for removed entries; mix/radio playlists that expire; region-dependent availability alerts.
Related errors
- Course %s does not exist
- %s: No songs found, try using proxy
- QQ Music said: error %d in fetching playlist info
- No chapters found for course %s
- No login info available, needed for using %s.
AI-assisted analysis of ytdl-org/youtube-dl@956b8c5855 (2026-08-14).
Data as JSON: /api/errors/896241a92e992f0d.
Report an issue: GitHub.