{"record":{"id":"cc23f66dfd46e4a2","repo":"ytdl-org/youtube-dl","slug":"error-description-cc23f6","errorCode":null,"errorMessage":"error['description']","messagePattern":"error\\['description'\\]","errorType":"exception","errorClass":"ExtractorError","httpStatus":401,"severity":"error","filePath":"youtube_dl/extractor/tv2.py","lineNumber":66,"sourceCode":"        title = asset.get('subtitle') or asset['title']\n        is_live = asset.get('live') is True\n\n        formats = []\n        format_urls = []\n        for protocol in self._PROTOCOLS:\n            try:\n                data = self._download_json(\n                    api_base + '/play.json?protocol=%s&videoFormat=SMIL+ISMUSP' % protocol,\n                    video_id, 'Downloading play JSON')['playback']\n            except ExtractorError as e:\n                if isinstance(e.cause, compat_HTTPError) and e.cause.code == 401:\n                    error = self._parse_json(e.cause.read().decode(), video_id)['error']\n                    error_code = error.get('code')\n                    if error_code == 'ASSET_PLAYBACK_INVALID_GEO_LOCATION':\n                        self.raise_geo_restricted(countries=self._GEO_COUNTRIES)\n                    elif error_code == 'SESSION_NOT_AUTHENTICATED':\n                        self.raise_login_required()\n                    raise ExtractorError(error['description'])\n                raise\n            items = try_get(data, lambda x: x['items']['item'])\n            if not items:\n                continue\n            if not isinstance(items, list):\n                items = [items]\n            for item in items:\n                if not isinstance(item, dict):\n                    continue\n                video_url = item.get('url')\n                if not video_url or video_url in format_urls:\n                    continue\n                format_id = '%s-%s' % (protocol.lower(), item.get('mediaFormat'))\n                if not self._is_valid_url(video_url, video_id, format_id):\n                    continue\n                format_urls.append(video_url)\n                ext = determine_ext(video_url)\n                if ext == 'f4m':","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/tv2.py#L48-L84","documentation":"Raised by TV2IE (Norwegian tv2.no) when the play.json API returns HTTP 401 and the parsed error is neither 'ASSET_PLAYBACK_INVALID_GEO_LOCATION' (which raises geo-restricted) nor 'SESSION_NOT_AUTHENTICATED' (which raises login-required). The error's 'description' field is re-raised verbatim (NOT expected=True).","triggerScenarios":"Extracting a tv2.no video whose /play.json returns 401 with some other code — commonly 'ASSET_PLAYBACK_INVALID_DEVICE_DRM', expired asset, or content requiring the TV2 Sumo subscription — after the extractor already fell back across protocols (hls/dash).","commonSituations":"Premium Sumo content accessed without a subscription; device-type DRM restrictions; Norway-only licensing; API changes after TV2's player updates that older extractors don't map.","solutions":["Read the description text — it states the actual refusal (DRM, subscription, expired)","If subscription content, log in via browser and pass cookies (yt-dlp --cookies-from-browser chrome)","If geo-related per the description, use a Norwegian exit IP","Update to yt-dlp for current TV2 handling"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    ydl.extract_info(tv2_url)\nexcept ExtractorError as e:\n    if 'ASSET_PLAYBACK' in str(e) or 'DRM' in str(e):\n        mark_premium_or_drm(url, str(e))\n    elif 'SESSION_NOT_AUTHENTICATED' in str(e) or 'login' in str(e):\n        retry_with_cookies(tv2_url)","preventionTips":["Read the description field — it distinguishes DRM, subscription, and geo cases","For Sumo content, log in via browser and reuse cookies","Use a Norwegian IP for Norway-licensed media","Note this error is not expected=True, so catch broadly on message content"],"tags":["extractor","http-401","api-error","auth","geo-restriction"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}