ytdl-org/youtube-dl · error · ExtractorError

error['description']

Error message

error['description']

What it means

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).

Source

Thrown at youtube_dl/extractor/tv2.py:66

        title = asset.get('subtitle') or asset['title']
        is_live = asset.get('live') is True

        formats = []
        format_urls = []
        for protocol in self._PROTOCOLS:
            try:
                data = self._download_json(
                    api_base + '/play.json?protocol=%s&videoFormat=SMIL+ISMUSP' % protocol,
                    video_id, 'Downloading play JSON')['playback']
            except ExtractorError as e:
                if isinstance(e.cause, compat_HTTPError) and e.cause.code == 401:
                    error = self._parse_json(e.cause.read().decode(), video_id)['error']
                    error_code = error.get('code')
                    if error_code == 'ASSET_PLAYBACK_INVALID_GEO_LOCATION':
                        self.raise_geo_restricted(countries=self._GEO_COUNTRIES)
                    elif error_code == 'SESSION_NOT_AUTHENTICATED':
                        self.raise_login_required()
                    raise ExtractorError(error['description'])
                raise
            items = try_get(data, lambda x: x['items']['item'])
            if not items:
                continue
            if not isinstance(items, list):
                items = [items]
            for item in items:
                if not isinstance(item, dict):
                    continue
                video_url = item.get('url')
                if not video_url or video_url in format_urls:
                    continue
                format_id = '%s-%s' % (protocol.lower(), item.get('mediaFormat'))
                if not self._is_valid_url(video_url, video_id, format_id):
                    continue
                format_urls.append(video_url)
                ext = determine_ext(video_url)
                if ext == 'f4m':

View on GitHub (pinned to 956b8c5855)

Solutions

  1. Read the description text — it states the actual refusal (DRM, subscription, expired)
  2. If subscription content, log in via browser and pass cookies (yt-dlp --cookies-from-browser chrome)
  3. If geo-related per the description, use a Norwegian exit IP
  4. Update to yt-dlp for current TV2 handling
Defensive patterns

Strategy: try-catch

Try / catch

try:
    ydl.extract_info(tv2_url)
except ExtractorError as e:
    if 'ASSET_PLAYBACK' in str(e) or 'DRM' in str(e):
        mark_premium_or_drm(url, str(e))
    elif 'SESSION_NOT_AUTHENTICATED' in str(e) or 'login' in str(e):
        retry_with_cookies(tv2_url)

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


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