{"record":{"id":"c1b3e27b9e7893d1","repo":"ytdl-org/youtube-dl","slug":"unable-to-login-s-c1b3e2","errorCode":null,"errorMessage":"Unable to login: %s","messagePattern":"Unable to login: (.+?)","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"youtube_dl/extractor/udemy.py","lineNumber":211,"sourceCode":"        login_form.update({\n            'email': username,\n            'password': password,\n        })\n\n        response = self._download_webpage(\n            self._LOGIN_URL, None, 'Logging in',\n            data=urlencode_postdata(login_form),\n            headers={\n                'Referer': self._ORIGIN_URL,\n                'Origin': self._ORIGIN_URL,\n            })\n\n        if not is_logged(response):\n            error = self._html_search_regex(\n                r'(?s)<div[^>]+class=\"form-errors[^\"]*\">(.+?)</div>',\n                response, 'error message', default=None)\n            if error:\n                raise ExtractorError('Unable to login: %s' % error, expected=True)\n            raise ExtractorError('Unable to log in')\n\n    def _real_extract(self, url):\n        lecture_id = self._match_id(url)\n\n        webpage = self._download_webpage(url, lecture_id)\n\n        course_id, _ = self._extract_course_info(webpage, lecture_id)\n\n        try:\n            lecture = self._download_lecture(course_id, lecture_id)\n        except ExtractorError as e:\n            # Error could possibly mean we are not enrolled in the course\n            if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403:\n                self._enroll_course(url, webpage, course_id)\n                lecture = self._download_lecture(course_id, lecture_id)\n            else:\n                raise","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/udemy.py#L193-L229","documentation":"Raised by UdemyIE._login when the POST to the login URL succeeded but the response does not look logged-in and the page contains a <div class=\"form-errors…\"> element. The scraped error text (e.g. invalid email/password) is appended to 'Unable to login: '. A sibling unexpected error 'Unable to log in' fires when no form-errors div is present.","triggerScenarios":"Running with --username/--password where authentication fails: wrong credentials, account locked, or Udemy rendering the failure inside the scraped form-errors div.","commonSituations":"Typo'd passwords, changed/expired passwords, spaces in shell-passed credentials, Udemy A/B markup that hides the div (leading to the generic variant).","solutions":["Verify the email/password pair logs in on udemy.com in a browser.","Quote credentials correctly on the command line (special characters, trailing spaces).","Prefer cookie auth: export cookies from a logged-in browser and use --cookies.","Update to yt-dlp if the login form/markup changed."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    ydl.extract_info(url)\nexcept ExtractorError as e:\n    if 'Unable to login' in str(e):\n        verify_credentials_in_browser(username)  # then switch to --cookies\n    else:\n        raise","preventionTips":["Confirm the exact credentials log in at udemy.com before scripting them.","Quote shell arguments properly; prefer netrc (--netrc) over inline passwords.","Fall back to cookie-based auth when form login fails."],"tags":["authentication","udemy","login","credentials"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}