ytdl-org/youtube-dl · error · ExtractorError
Unable to login: %s
Error message
Unable to login: %s
What it means
Thrown during Lynda authentication when a step of the multi-stage login flow returns a JSON body containing an error under one of the watched keys (passed as key_or_keys to _check_error). It is expected=True, so it signals a credential or account problem (bad password, expired account, captcha) rather than an internal failure. The '%s' is the raw error string from lynda.com.
Source
Thrown at youtube_dl/extractor/lynda.py:33
class LyndaBaseIE(InfoExtractor):
_SIGNIN_URL = 'https://www.lynda.com/signin/lynda'
_PASSWORD_URL = 'https://www.lynda.com/signin/password'
_USER_URL = 'https://www.lynda.com/signin/user'
_ACCOUNT_CREDENTIALS_HINT = 'Use --username and --password options to provide lynda.com account credentials.'
_NETRC_MACHINE = 'lynda'
def _real_initialize(self):
self._login()
@staticmethod
def _check_error(json_string, key_or_keys):
keys = [key_or_keys] if isinstance(key_or_keys, compat_str) else key_or_keys
for key in keys:
error = json_string.get(key)
if error:
raise ExtractorError('Unable to login: %s' % error, expected=True)
def _login_step(self, form_html, fallback_action_url, extra_form_data, note, referrer_url):
action_url = self._search_regex(
r'<form[^>]+action=(["\'])(?P<url>.+?)\1', form_html,
'post url', default=fallback_action_url, group='url')
if not action_url.startswith('http'):
action_url = compat_urlparse.urljoin(self._SIGNIN_URL, action_url)
form_data = self._hidden_inputs(form_html)
form_data.update(extra_form_data)
response = self._download_json(
action_url, None, note,
data=urlencode_postdata(form_data),
headers={
'Referer': referrer_url,
'X-Requested-With': 'XMLHttpRequest',View on GitHub (pinned to 956b8c5855)
Solutions
- Verify the lynda.com credentials are correct by logging in with a browser.
- Provide credentials explicitly: --username and --password, or a netrc entry for machine 'lynda'.
- Pass --cookies with an exported browser cookie jar to bypass the form login entirely.
- If credentials are valid but the error persists, the login flow likely changed; update youtube-dl (the Lynda extractor is frequently patched) or report an issue.
Defensive patterns
Strategy: validation
Validate before calling
# Validate credentials shape before invoking the extractor
import getpass, netrc
try:
n = netrc.netrc()
auth = n.authenticators('lynda')
ok = bool(auth and auth[0] and auth[2])
except FileNotFoundError:
ok = '--username' in sys.argv or False
assert ok, 'Provide lynda credentials via --username/--password or a netrc entry' Type guard
def has_lynda_credentials(opts):
return bool(getattr(opts, 'username', None) and getattr(opts, 'password', None)) or getattr(opts, 'cookiefile', None) is not None Try / catch
try:
ydl.download([url])
except ExtractorError as e:
if 'Unable to login' in str(e):
# credentials/account problem: fix inputs, do not retry unchanged
print('Check lynda credentials or supply --cookies')
else:
raise Prevention
- Store lynda credentials in a netrc entry (machine lynda) instead of CLI flags.
- Prefer --cookies exported from a logged-in browser to sidestep the form-login flow.
- Log in manually at lynda.com first to clear captchas/2FA before extracting.
When it happens
Trigger: Any _login_step POST whose response JSON contains a truthy value under the checked key(s) (e.g. 'error', 'ErrorMessage') triggers this immediately after the form is submitted with the user's credentials.
Common situations: Wrong username/password supplied via --username/--password or .netrc (machine 'lynda'); expired or cancelled Lynda subscription; Lynda login flow changed (new CSRF field, new endpoint) so the parsed response now contains an error blob; forced captcha/2FA on the account.
Related errors
- We're sorry, but either the User ID or Password entered is n
- Unable to login: %s said: %s
- Unable to login: %s
- Unable to login: %s
- Unable to login: %s
AI-assisted analysis of ytdl-org/youtube-dl@956b8c5855 (2026-08-14).
Data as JSON: /api/errors/cbc3a44a1be74784.
Report an issue: GitHub.