ytdl-org/youtube-dl · error · ExtractorError

Unable to log in

Error message

Unable to log in

What it means

The fallback Platzi login failure: raised when the post-login page was fetched and parsed successfully but none of the error/password/nonFields fields in the login JSON is set — i.e. the response indicates failure without a specific message. Unlike the sibling errors it is NOT marked expected, so the downloader treats it as an unexpected login failure worth reporting as a bug.

Solutions

  1. Log in manually at platzi.com to rule out account issues (unverified email, captcha, password change)
  2. Export browser cookies and pass --cookies instead of credential login
  3. Update yt-dlp (extractor maintained there) and if current, report the site login change upstream

Example fix

# before
youtube_dl --username me@example.com --password '...' PLATZI_URL
# after
# export cookies from a logged-in browser session, then:
youtube_dl --cookies cookies.txt PLATZI_URL
Defensive patterns

Strategy: fallback

Try / catch

try:
    ydl.extract_info(url, username=..., password=...)
except ExtractorError as e:
    if 'Unable to log in' in str(e):  # not expected → likely site change
        # fall back to browser-session cookies
        ydl2 = YoutubeDL({'cookiefile': 'cookies.txt')
        info = ydl2.extract_info(url)

Prevention

When it happens

Trigger: Platzi login POST succeeds at HTTP level but the returned page's login JSON has no error fields — typically an undocumented state: maintenance page, captcha/2FA interstitial, or an A/B login flow the extractor does not recognize.

Common situations: Platzi ships a new login flow (e.g. React redirect without the scraped JSON); rate limiting or Cloudflare challenge on the login endpoint; account state like unverified email that shows a non-standard page.

Related errors


AI-assisted analysis of ytdl-org/youtube-dl@956b8c5855 (2026-08-14). Data as JSON: /api/errors/d8b61c80a71c488c. Report an issue: GitHub.

Appendix: source

Thrown at youtube_dl/extractor/platzi.py:65

        # login succeeded
        if 'platzi.com/login' not in urlh.geturl():
            return

        login_error = self._webpage_read_content(
            urlh, self._LOGIN_URL, None, 'Downloading login error page')

        login = self._parse_json(
            self._search_regex(
                r'login\s*=\s*({.+?})(?:\s*;|\s*</script)', login_error, 'login'),
            None)

        for kind in ('error', 'password', 'nonFields'):
            error = str_or_none(login.get('%sError' % kind))
            if error:
                raise ExtractorError(
                    'Unable to login: %s' % error, expected=True)
        raise ExtractorError('Unable to log in')


class PlatziIE(PlatziBaseIE):
    _VALID_URL = r'''(?x)
                    https?://
                        (?:
                            platzi\.com/clases|           # es version
                            courses\.platzi\.com/classes  # en version
                        )/[^/]+/(?P<id>\d+)-[^/?\#&]+
                    '''

    _TESTS = [{
        'url': 'https://platzi.com/clases/1311-next-js/12074-creando-nuestra-primera-pagina/',
        'md5': '8f56448241005b561c10f11a595b37e3',
        'info_dict': {
            'id': '12074',
            'ext': 'mp4',
            'title': 'Creando nuestra primera página',

View on GitHub (pinned to 956b8c5855)