yt-dlp/yt-dlp · error · ExtractorError

Unable to login: {credentials}

Error message

Unable to login: {credentials}

What it means

The O'Reilly/Safari login POSTs email/password to the auth API (an HTTP 400 response is tolerated). If the response says logged_in=false, has no redirect_uri, and includes a 'credentials' object, that object carries the server's failure text and is surfaced as 'Unable to login: <credentials>' (expected=True).

Source

Thrown at yt_dlp/extractor/safari.py:53

        qs = urllib.parse.parse_qs(parsed_url.query)
        next_uri = urllib.parse.urljoin(
            'https://api.oreilly.com', qs['next'][0])

        auth, urlh = self._download_json_handle(
            'https://www.oreilly.com/member/auth/login/', None, 'Logging in',
            data=json.dumps({
                'email': username,
                'password': password,
                'redirect_uri': next_uri,
            }).encode(), headers={
                'Content-Type': 'application/json',
                'Referer': redirect_url,
            }, expected_status=400)

        credentials = auth.get('credentials')
        if (not auth.get('logged_in') and not auth.get('redirect_uri')
                and credentials):
            raise ExtractorError(
                f'Unable to login: {credentials}', expected=True)

        # oreilly serves two same instances of the following cookies
        # in Set-Cookie header and expects first one to be actually set
        for cookie in ('groot_sessionid', 'orm-jwt', 'orm-rt'):
            self._apply_first_set_cookie_header(urlh, cookie)

        _, urlh = self._download_webpage_handle(
            auth.get('redirect_uri') or next_uri, None, 'Completing login')

        if is_logged(urlh):
            self.LOGGED_IN = True
            return

        raise ExtractorError('Unable to log in')


class SafariIE(SafariBaseIE):

View on GitHub (pinned to 81ecd58b13)

Solutions

  1. Verify the email/password by logging in at learning.oreilly.com in a browser
  2. If your account is organizational/SSO-only, password login cannot work — use an account with a native password
  3. Re-enter credentials carefully (quote them properly in the shell)
  4. Update yt-dlp in case the auth endpoint changed
Defensive patterns

Strategy: try-catch

Try / catch

from yt_dlp.utils import ExtractorError

try:
    info = ydl.extract_info(url, download=True)
except ExtractorError as e:
    if e.expected and 'Unable to login:' in str(e):
        # O'Reilly's own rejection (wrong password / SSO-only account)
        raise SystemExit(f'O\'Reilly rejected the login: {e}')
    raise

Prevention

When it happens

Trigger: Running a safari/oreilly extractor with --username/--password where O'Reilly rejects the pair: the auth API answers 400 with {'logged_in': false, 'credentials': {...}} — wrong password, unknown email, or an account without a native password.

Common situations: Wrong or expired password; accounts that authenticate only via organization SSO (no native password exists); stale .netrc credentials.

Related errors


AI-assisted analysis of yt-dlp/yt-dlp@81ecd58b13 (2026-08-22). Data as JSON: /api/errors/1cbab5d0b3633f21. Report an issue: GitHub.