{"record":{"id":"e66a74dd9bca8cf0","repo":"ytdl-org/youtube-dl","slug":"unable-to-login-s-e66a74","errorCode":null,"errorMessage":"Unable to login: %s","messagePattern":"Unable to login: (.+?)","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"youtube_dl/extractor/bbc.py","lineNumber":294,"sourceCode":"        login_form = self._hidden_inputs(login_page)\n\n        login_form.update({\n            'username': username,\n            'password': password,\n        })\n\n        post_url = urljoin(self._LOGIN_URL, self._search_regex(\n            r'<form[^>]+action=([\"\\'])(?P<url>.+?)\\1', login_page,\n            'post url', default=self._LOGIN_URL, group='url'))\n\n        response, urlh = self._download_webpage_handle(\n            post_url, None, 'Logging in', data=urlencode_postdata(login_form),\n            headers={'Referer': self._LOGIN_URL})\n\n        if self._LOGIN_URL in urlh.geturl():\n            error = clean_html(get_element_by_class('form-message', response))\n            if error:\n                raise ExtractorError(\n                    'Unable to login: %s' % error, expected=True)\n            raise ExtractorError('Unable to log in')\n\n    def _real_initialize(self):\n        self._login()\n\n    class MediaSelectionError(Exception):\n        def __init__(self, id):\n            self.id = id\n\n    def _extract_asx_playlist(self, connection, programme_id):\n        asx = self._download_xml(connection.get('href'), programme_id, 'Downloading ASX playlist')\n        return [ref.get('href') for ref in asx.findall('./Entry/ref')]\n\n    def _extract_items(self, playlist):\n        return playlist.findall('./{%s}item' % self._EMP_PLAYLIST_NS)\n\n    def _extract_medias(self, media_selection):","sourceCodeStart":276,"sourceCodeEnd":312,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/bbc.py#L276-L312","documentation":"Raised by BBCCoUkIE._login after POSTing credentials: the extractor checks whether the final URL of the login response still contains the login URL. If it does, login failed, and it scrapes the 'form-message' element from the response page; when a message is found, this error wraps it (e.g. 'Unable to login: Invalid username or password'). It is marked expected=True, meaning it is a normal user-facing failure, not a bug.","triggerScenarios":"Configuring youtube-dl with BBC iPlayer account credentials via --username/--password (or extractor args) where the POST to the form action redirects back to the BBC sign-in page and the rendered HTML contains an element of class 'form-message' with error text. Typical causes: wrong password, expired/blocked account, or BBC changing its sign-in flow so the POST no longer completes.","commonSituations":"Wrong or stale credentials in .config/youtube-dl or netrc; account locked after failed attempts; BBC migrated login to a new domain/form so the scraped form action posts to a dead endpoint; credentials containing characters mangled by urlencode_postdata handling.","solutions":["Re-enter credentials manually on bbc.com/account/signin to confirm the username/password are valid and the account is not locked.","Delete stale cached credentials (netrc, config files, cookies.txt) and re-pass --username/--password explicitly.","If credentials are correct, inspect the POST target: BBC may have changed the form action or added CSRF fields; update _login (bbc.py:294) to parse the new form.","Most BBC content is downloadable without login; retry without credentials before debugging the login path."],"exampleFix":"// before\nresponse, urlh = self._download_webpage_handle(post_url, None, 'Logging in', data=urlencode_postdata(login_form), headers={'Referer': self._LOGIN_URL})\nif self._LOGIN_URL in urlh.geturl():\n    error = clean_html(get_element_by_class('form-message', response))\n    if error:\n        raise ExtractorError('Unable to login: %s' % error, expected=True)\n\n// after: also surface the destination URL to disambiguate 'bad credentials' from 'flow changed'\nif self._LOGIN_URL in urlh.geturl():\n    error = clean_html(get_element_by_class('form-message', response))\n    if error:\n        raise ExtractorError('Unable to login: %s' % error, expected=True)\n    raise ExtractingError  # placeholder - see below\nraise ExtractorError('Unable to log in (unexpected redirect to %s); BBC sign-in flow may have changed' % urlh.geturl(), expected=True)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    ydl.extract_info(url, username=USER, password=PASS)\nexcept ExtractorError as e:\n    msg = str(e)\n    if msg.startswith('Unable to login:'):\n        # expected=True credential failure; surface BBC's form message, do not retry\n        show_user(msg.split(':', 1)[1].strip())\n    else:\n        raise","preventionTips":["Verify BBC credentials manually in a browser before configuring them in youtube-dl.","Keep credentials in a netrc/config file rather than ad-hoc CLI flags to avoid typos.","Only pass credentials for BBC URLs; most programmes extract without login."],"tags":["bbc","authentication","login","credentials","html-scraping"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}