ytdl-org/youtube-dl · error · ExtractorError

Unable to login: %s

Error message

Unable to login: %s

What it means

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.

Source

Thrown at youtube_dl/extractor/bbc.py:294

        login_form = self._hidden_inputs(login_page)

        login_form.update({
            'username': username,
            'password': password,
        })

        post_url = urljoin(self._LOGIN_URL, self._search_regex(
            r'<form[^>]+action=(["\'])(?P<url>.+?)\1', login_page,
            'post url', default=self._LOGIN_URL, group='url'))

        response, urlh = self._download_webpage_handle(
            post_url, None, 'Logging in', data=urlencode_postdata(login_form),
            headers={'Referer': self._LOGIN_URL})

        if self._LOGIN_URL in urlh.geturl():
            error = clean_html(get_element_by_class('form-message', response))
            if error:
                raise ExtractorError(
                    'Unable to login: %s' % error, expected=True)
            raise ExtractorError('Unable to log in')

    def _real_initialize(self):
        self._login()

    class MediaSelectionError(Exception):
        def __init__(self, id):
            self.id = id

    def _extract_asx_playlist(self, connection, programme_id):
        asx = self._download_xml(connection.get('href'), programme_id, 'Downloading ASX playlist')
        return [ref.get('href') for ref in asx.findall('./Entry/ref')]

    def _extract_items(self, playlist):
        return playlist.findall('./{%s}item' % self._EMP_PLAYLIST_NS)

    def _extract_medias(self, media_selection):

View on GitHub (pinned to 956b8c5855)

Solutions

  1. Re-enter credentials manually on bbc.com/account/signin to confirm the username/password are valid and the account is not locked.
  2. Delete stale cached credentials (netrc, config files, cookies.txt) and re-pass --username/--password explicitly.
  3. 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.
  4. Most BBC content is downloadable without login; retry without credentials before debugging the login path.

Example fix

// before
response, urlh = self._download_webpage_handle(post_url, None, 'Logging in', data=urlencode_postdata(login_form), headers={'Referer': self._LOGIN_URL})
if self._LOGIN_URL in urlh.geturl():
    error = clean_html(get_element_by_class('form-message', response))
    if error:
        raise ExtractorError('Unable to login: %s' % error, expected=True)

// after: also surface the destination URL to disambiguate 'bad credentials' from 'flow changed'
if self._LOGIN_URL in urlh.geturl():
    error = clean_html(get_element_by_class('form-message', response))
    if error:
        raise ExtractorError('Unable to login: %s' % error, expected=True)
    raise ExtractingError  # placeholder - see below
raise ExtractorError('Unable to log in (unexpected redirect to %s); BBC sign-in flow may have changed' % urlh.geturl(), expected=True)
Defensive patterns

Strategy: try-catch

Try / catch

try:
    ydl.extract_info(url, username=USER, password=PASS)
except ExtractorError as e:
    msg = str(e)
    if msg.startswith('Unable to login:'):
        # expected=True credential failure; surface BBC's form message, do not retry
        show_user(msg.split(':', 1)[1].strip())
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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