{"record":{"id":"1cbab5d0b3633f21","repo":"yt-dlp/yt-dlp","slug":"unable-to-login-credentials","errorCode":null,"errorMessage":"Unable to login: {credentials}","messagePattern":"Unable to login: (.+?)","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"yt_dlp/extractor/safari.py","lineNumber":53,"sourceCode":"        qs = urllib.parse.parse_qs(parsed_url.query)\n        next_uri = urllib.parse.urljoin(\n            'https://api.oreilly.com', qs['next'][0])\n\n        auth, urlh = self._download_json_handle(\n            'https://www.oreilly.com/member/auth/login/', None, 'Logging in',\n            data=json.dumps({\n                'email': username,\n                'password': password,\n                'redirect_uri': next_uri,\n            }).encode(), headers={\n                'Content-Type': 'application/json',\n                'Referer': redirect_url,\n            }, expected_status=400)\n\n        credentials = auth.get('credentials')\n        if (not auth.get('logged_in') and not auth.get('redirect_uri')\n                and credentials):\n            raise ExtractorError(\n                f'Unable to login: {credentials}', expected=True)\n\n        # oreilly serves two same instances of the following cookies\n        # in Set-Cookie header and expects first one to be actually set\n        for cookie in ('groot_sessionid', 'orm-jwt', 'orm-rt'):\n            self._apply_first_set_cookie_header(urlh, cookie)\n\n        _, urlh = self._download_webpage_handle(\n            auth.get('redirect_uri') or next_uri, None, 'Completing login')\n\n        if is_logged(urlh):\n            self.LOGGED_IN = True\n            return\n\n        raise ExtractorError('Unable to log in')\n\n\nclass SafariIE(SafariBaseIE):","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/yt-dlp/yt-dlp/blob/81ecd58b1394793e6da9998cc19fdb45657f1685/yt_dlp/extractor/safari.py#L35-L71","documentation":"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).","triggerScenarios":"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.","commonSituations":"Wrong or expired password; accounts that authenticate only via organization SSO (no native password exists); stale .netrc credentials.","solutions":["Verify the email/password by logging in at learning.oreilly.com in a browser","If your account is organizational/SSO-only, password login cannot work — use an account with a native password","Re-enter credentials carefully (quote them properly in the shell)","Update yt-dlp in case the auth endpoint changed"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"from yt_dlp.utils import ExtractorError\n\ntry:\n    info = ydl.extract_info(url, download=True)\nexcept ExtractorError as e:\n    if e.expected and 'Unable to login:' in str(e):\n        # O'Reilly's own rejection (wrong password / SSO-only account)\n        raise SystemExit(f'O\\'Reilly rejected the login: {e}')\n    raise","preventionTips":["Verify email/password at learning.oreilly.com before scripting","Know whether your account is SSO-only — those cannot use password login","Keep credentials in .netrc and update them when the site password changes"],"tags":["authentication","invalid-credentials","safari","oreilly"],"backgroundTag":"invalid-credentials","analyzedSha":"81ecd58b1394793e6da9998cc19fdb45657f1685","analyzedAt":"2026-08-22T12:21:25.439Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}