ytdl-org/youtube-dl · error · ExtractorError

resp.get('userMessage') or resp['id']

Error message

resp.get('userMessage') or resp['id']

What it means

Raised by the Globo extractor's login helper when the auth API returns HTTP 401: it parses the error body and re-raises with its 'userMessage' (falling back to 'id'). Marked expected=True. Note the fallback resp['id'] will itself KeyError if the 401 body contains neither field.

Source

Thrown at youtube_dl/extractor/globo.py:90

            return

        try:
            glb_id = (self._download_json(
                'https://login.globo.com/api/authentication', None, data=json.dumps({
                    'payload': {
                        'email': email,
                        'password': password,
                        'serviceId': 4654,
                    },
                }).encode(), headers={
                    'Content-Type': 'application/json; charset=utf-8',
                }) or {}).get('glbId')
            if glb_id:
                self._set_cookie('.globo.com', 'GLBID', glb_id)
        except ExtractorError as e:
            if isinstance(e.cause, compat_HTTPError) and e.cause.code == 401:
                resp = self._parse_json(e.cause.read(), None)
                raise ExtractorError(resp.get('userMessage') or resp['id'], expected=True)
            raise

    def _real_extract(self, url):
        video_id = self._match_id(url)

        video = self._download_json(
            'http://api.globovideos.com/videos/%s/playlist' % video_id,
            video_id)['videos'][0]
        if video.get('encrypted') is True:
            raise ExtractorError('This video is DRM protected.', expected=True)

        title = video['title']

        formats = []
        subtitles = {}
        for resource in video['resources']:
            resource_id = resource.get('_id')
            resource_url = resource.get('url')

View on GitHub (pinned to 956b8c5855)

Solutions

  1. Confirm your Globo credentials work in a browser on the same content
  2. Ensure the account has the subscription required for that video
  3. Re-enter credentials (--username/--password or netrc) to rule out stale ones
  4. Update youtube-dl/yt-dlp for auth endpoint changes
Defensive patterns

Strategy: try-catch

Validate before calling

import requests
r = requests.post(globo_auth_url, json=payload, headers={'Content-Type': 'application/json; charset=utf-8'})
if r.status_code == 401:
    body = r.json()
    print('Globo auth refused:', body.get('userMessage') or body.get('id'))

Type guard

def globo_auth_ok(resp_json):
    return isinstance(resp_json, dict) and 'glbId' in resp_json

Try / catch

try:
    ydl.extract_info(url)
except DownloadError as e:
    if 'glbId' in str(e) or 'userMessage' in str(e) or 'Globo' in str(e):
        reauth_and_retry(url)  # 401 = credentials/entitlement
    else:
        raise

Prevention

When it happens

Trigger: POSTing email/password to Globo's glbId endpoint with invalid credentials, an unentitled account (no Globoplay subscription for the content), or an expired session, yielding 401 with a JSON body.

Common situations: Wrong password, account without the required subscription, Globo changing its auth endpoint/response, or a 401 from rate-limiting whose body lacks userMessage/id (causing the secondary KeyError).

Related errors


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