{"record":{"id":"fe033cc24cad3a8d","repo":"ytdl-org/youtube-dl","slug":"resp-get-usermessage-or-resp-id","errorCode":null,"errorMessage":"resp.get('userMessage') or resp['id']","messagePattern":"resp\\.get\\('userMessage'\\) or resp\\['id'\\]","errorType":"exception","errorClass":"ExtractorError","httpStatus":401,"severity":"error","filePath":"youtube_dl/extractor/globo.py","lineNumber":90,"sourceCode":"            return\n\n        try:\n            glb_id = (self._download_json(\n                'https://login.globo.com/api/authentication', None, data=json.dumps({\n                    'payload': {\n                        'email': email,\n                        'password': password,\n                        'serviceId': 4654,\n                    },\n                }).encode(), headers={\n                    'Content-Type': 'application/json; charset=utf-8',\n                }) or {}).get('glbId')\n            if glb_id:\n                self._set_cookie('.globo.com', 'GLBID', glb_id)\n        except ExtractorError as e:\n            if isinstance(e.cause, compat_HTTPError) and e.cause.code == 401:\n                resp = self._parse_json(e.cause.read(), None)\n                raise ExtractorError(resp.get('userMessage') or resp['id'], expected=True)\n            raise\n\n    def _real_extract(self, url):\n        video_id = self._match_id(url)\n\n        video = self._download_json(\n            'http://api.globovideos.com/videos/%s/playlist' % video_id,\n            video_id)['videos'][0]\n        if video.get('encrypted') is True:\n            raise ExtractorError('This video is DRM protected.', expected=True)\n\n        title = video['title']\n\n        formats = []\n        subtitles = {}\n        for resource in video['resources']:\n            resource_id = resource.get('_id')\n            resource_url = resource.get('url')","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/globo.py#L72-L108","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","solutions":["Confirm your Globo credentials work in a browser on the same content","Ensure the account has the subscription required for that video","Re-enter credentials (--username/--password or netrc) to rule out stale ones","Update youtube-dl/yt-dlp for auth endpoint changes"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"import requests\nr = requests.post(globo_auth_url, json=payload, headers={'Content-Type': 'application/json; charset=utf-8'})\nif r.status_code == 401:\n    body = r.json()\n    print('Globo auth refused:', body.get('userMessage') or body.get('id'))","typeGuard":"def globo_auth_ok(resp_json):\n    return isinstance(resp_json, dict) and 'glbId' in resp_json","tryCatchPattern":"try:\n    ydl.extract_info(url)\nexcept DownloadError as e:\n    if 'glbId' in str(e) or 'userMessage' in str(e) or 'Globo' in str(e):\n        reauth_and_retry(url)  # 401 = credentials/entitlement\n    else:\n        raise","preventionTips":["Confirm the account's Globoplay tier covers premium content before automating","Re-run login when you change the account password","Treat 401s as expected auth failures, not extractor bugs"],"tags":["authentication","globo","http-401","login"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}