ytdl-org/youtube-dl · error · ExtractorError
reason (dynamic YouTube playability status reason, with opti
Error message
reason (dynamic YouTube playability status reason, with optional subreason/captcha suffix)
What it means
The dynamic playability-status failure in YoutubeIE._real_extract: when no formats were obtained and no DRM licenseInfos exist, the extractor builds 'reason' from playability_status (errorScreen.playerErrorMessageRenderer.reason or playability_status.reason), optionally appends a cleaned subreason, escalates geo-restriction via raise_geo_restricted or login via raise_login_required (when 'sign in' appears), notes captcha challenges, and finally raises ExtractorError(reason, expected=True). The message is whatever YouTube reported.
Source
Thrown at youtube_dl/extractor/youtube.py:2807
reason = get_text(pemr.get('reason')) or playability_status.get('reason') or ''
subreason = pemr.get('subreason')
if subreason:
subreason = clean_html(get_text(subreason))
if subreason.startswith('The uploader has not made this video available in your country'):
countries = microformat.get('availableCountries')
if not countries:
regions_allowed = search_meta('regionsAllowed')
countries = regions_allowed.split(',') if regions_allowed else None
self.raise_geo_restricted(
subreason, countries)
reason += '\n' + subreason
if reason:
if 'sign in' in reason.lower():
self.raise_login_required(remove_end(reason, 'This helps protect our community. Learn more'))
elif traverse_obj(playability_status, ('errorScreen', 'playerCaptchaViewModel', T(dict))):
reason += '. YouTube is requiring a captcha challenge before playback'
raise ExtractorError(reason, expected=True)
self._sort_formats(formats)
keywords = video_details.get('keywords') or []
if not keywords and webpage:
keywords = [
unescapeHTML(m.group('content'))
for m in re.finditer(self._meta_regex('og:video:tag'), webpage)]
for keyword in keywords:
if keyword.startswith('yt:stretch='):
mobj = re.search(r'(\d+)\s*:\s*(\d+)', keyword)
if mobj:
# NB: float is intentional for forcing float division
w, h = (float(v) for v in mobj.groups())
if w > 0 and h > 0:
ratio = w / h
for f in formats:
if f.get('vcodec') != 'none':View on GitHub (pinned to 956b8c5855)
Solutions
- Read the reason text — it is YouTube's own explanation and dictates the fix.
- For sign-in/age reasons, pass cookies: --cookies-from-browser or --username/--password.
- For geo blocks, route through an allowed region or accept the restriction; availableCountries/microformat informs which.
- For captchas, slow down / use residential egress; for removed/private, no client-side fix exists.
Defensive patterns
Strategy: try-catch
Type guard
def playability_is_fatal(player_response: dict) -> bool:
status = traverse_obj(player_response, ('playabilityStatus', 'status'))
return status in ('UNPLAYABLE', 'LOGIN_REQUIRED', 'ERROR') Try / catch
try:
info = ydl.extract_info(url)
except ExtractorError as e:
msg = str(e)
if 'sign in' in msg.lower() or 'confirm your age' in msg.lower():
retry_with_cookies(url) # LOGIN_REQUIRED / age gate
elif 'not made this video available in your country' in msg:
skip_or_reroute_region(url) # geo block
else:
raise Prevention
- Supply cookies up front for age-gated and members-only content instead of reacting to failures.
- Classify on the reason text (sign in / country / captcha) — each has a different remedy.
When it happens
Trigger: Videos with playabilityStatus.status like UNPLAYABLE/LOGIN_REQUIRED/ERROR: private videos, removed videos, sign-in-required (age) videos, country-locked videos (subreason starting 'The uploader has not made this video available in your country'), and captcha challenges (playerCaptchaViewModel).
Common situations: Age-restricted videos without login; region locks for users in blocked countries; members-only or private uploads; YouTube occasionally forcing captchas on datacenter IPs.
Related errors
- traceback.format_exc() (dynamic; surfaces in 'Unable to deco
- %s
- %s
- %s said: %s
- Video %s is no longer available
AI-assisted analysis of ytdl-org/youtube-dl@956b8c5855 (2026-08-14).
Data as JSON: /api/errors/548951b4bec5d004.
Report an issue: GitHub.