yt-dlp/yt-dlp · error · ExtractorError

{error_msg}{login_hint}

Error message

{error_msg}{login_hint}

What it means

Weverse does not accept email/password in yt-dlp. Login works via OAuth tokens: the username must start with the literal prefix 'oauth' (e.g. oauth+LABEL), and the password must be the refresh token copied from the browser cookie. _report_login_error wraps the mapped error message with a hint that spells out the exact --username/--password values or the session-cookie alternative.

Source

Thrown at yt_dlp/extractor/weverse.py:159

    def _get_authorization_header(self):
        if not self._is_logged_in:
            return {}
        if self._token_is_expired(self._ACCESS_TOKEN_KEY):
            self._refresh_access_token()
        return {'Authorization': f'Bearer {self._oauth_tokens[self._ACCESS_TOKEN_KEY]}'}

    def _report_login_error(self, error_id):
        error_msg = self._LOGIN_ERRORS_MAP[error_id]
        username = self._get_login_info()[0]

        if error_id == 'invalid_username':
            error_msg = error_msg.format(username)
            username = f'{self._OAUTH_PREFIX}+{username}'
        elif not username:
            username = f'{self._OAUTH_PREFIX}+USERNAME'

        raise ExtractorError(join_nonempty(
            error_msg, self._LOGIN_HINT_TMPL.format(self._OAUTH_PREFIX, self._REFRESH_TOKEN_KEY, username),
            'Or else you can u', self._login_hint(method='session_cookies')[1:], delim=''), expected=True)

    def _perform_login(self, username, password):
        if self._is_logged_in:
            return

        if username.partition('+')[0] != self._OAUTH_PREFIX:
            self._report_login_error('invalid_username')

        self._oauth_tokens.update(self.cache.load(self._NETRC_MACHINE, self._oauth_cache_key, default={}))
        if self._is_logged_in and self._access_token_is_valid():
            return

        rt_key = self._REFRESH_TOKEN_KEY
        if not self._oauth_tokens.get(rt_key) or self._token_is_expired(rt_key):
            if try_call(lambda: jwt_decode_hs256(password)['scope']) != 'refresh':
                self._report_login_error('invalid_password')

View on GitHub (pinned to 81ecd58b13)

Solutions

  1. Follow the hint embedded in the error text: --username 'oauth+ANY_LABEL' --password 'REFRESH_TOKEN' where REFRESH_TOKEN is the value of the cookie named in the message from a logged-in browser
  2. Run 'yt-dlp --rm-cache-dir' to drop stale cached Weverse tokens, then log in again with a fresh refresh token
  3. Alternatively export session cookies from a logged-in browser with --cookies-from-browser
  4. Update yt-dlp - the token scheme and hint text have changed across releases

Example fix

# before: email/password is not a valid Weverse login for yt-dlp
yt-dlp --username fan@mail.com --password hunter2 'https://weverse.io/artist/live/1-123'
# after: oauth-prefixed username + refresh token cookie value
yt-dlp --username 'oauth+fan@mail.com' --password 'REFRESH_TOKEN_FROM_BROWSER_COOKIE' 'https://weverse.io/artist/live/1-123'
Defensive patterns

Strategy: try-catch

Validate before calling

# Weverse login contract: username must start with 'oauth+' and the
# password must be the refresh token cookie value, not the account password.
if not username.startswith('oauth+'):
    raise ValueError("Weverse username must be 'oauth+LABEL', not an email")
if password == account_password:
    raise ValueError('Weverse password must be the refresh token cookie value from the browser')

Type guard

def is_weverse_login_hint(exc: Exception) -> bool:
    return isinstance(exc, ExtractorError) and (
        'not valid login username' in str(exc)
        or 'valid refresh token' in str(exc)
        or 'logged-in users' in str(exc)
        or 'refresh token' in str(exc))

Try / catch

try:
    with YoutubeDL({'username': 'oauth+main', 'password': refresh_token}) as ydl:
        ydl.download([url])
except DownloadError as e:
    if 'refresh token' in str(e) or 'login' in str(e):
        refresh_token = fetch_fresh_refresh_token_cookie()  # from the logged-in browser
        subprocess.run(['yt-dlp', '--rm-cache-dir'])
        retry_with(refresh_token)
    else:
        raise

Prevention

When it happens

Trigger: Running with --username that does not start with 'oauth+' (e.g. an email address), passing an access token instead of the refresh token as the password, an expired cached refresh token, or hitting members-only content with no login at all - each maps to an id in _LOGIN_ERRORS_MAP and this combined message-plus-hint.

Common situations: Trying email+password like a normal site; copying the wrong cookie value; refresh tokens expiring after web re-login; stale cached tokens on disk.

Related errors


AI-assisted analysis of yt-dlp/yt-dlp@81ecd58b13 (2026-08-22). Data as JSON: /api/errors/be213823909a7df7. Report an issue: GitHub.