ytdl-org/youtube-dl · error · ExtractorError

%s

Error message

%s

What it means

Raised by the SevenplusIE extractor when the 7plus Brightcove playback API (videoservice.swm.digital) answers an HTTP 403. The body of the 403 response is parsed as JSON and the first entry's 'error_code' field is surfaced as the error message (e.g. GEO_RESTRICTED or content-unavailable codes). It is marked expected=True, so youtube-dl reports it as a known, user-facing failure rather than a bug.

Source

Thrown at youtube_dl/extractor/sevenplus.py:61

    }]

    def _real_extract(self, url):
        path, episode_id = re.match(self._VALID_URL, url).groups()

        try:
            media = self._download_json(
                'https://videoservice.swm.digital/playback', episode_id, query={
                    'appId': '7plus',
                    'deviceType': 'web',
                    'platformType': 'web',
                    'accountId': 5303576322001,
                    'referenceId': 'ref:' + episode_id,
                    'deliveryId': 'csai',
                    'videoType': 'vod',
                })['media']
        except ExtractorError as e:
            if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403:
                raise ExtractorError(self._parse_json(
                    e.cause.read().decode(), episode_id)[0]['error_code'], expected=True)
            raise

        for source in media.get('sources', {}):
            src = source.get('src')
            if not src:
                continue
            source['src'] = update_url_query(src, {'rule': ''})

        info = self._parse_brightcove_metadata(media, episode_id)

        content = self._download_json(
            'https://component-cdn.swm.digital/content/' + path,
            episode_id, headers={
                'market-id': 4,
            }, fatal=False) or {}
        for item in content.get('items', {}):
            if item.get('componentData', {}).get('componentType') == 'infoPanel':

View on GitHub (pinned to 956b8c5855)

Solutions

  1. If outside Australia, route through an Australian VPN/proxy or accept the geo block.
  2. Re-fetch the episode page to get a fresh episode_id/referenceId and retry with the new URL.
  3. Inspect e.cause.read() manually (curl the playback endpoint) to confirm which error_code the API returns before assuming geo-blocking.
  4. If the 403 body is no longer JSON, update the extractor to tolerate non-JSON error bodies.

Example fix

// before
raise ExtractorError(self._parse_json(
    e.cause.read().decode(), episode_id)[0]['error_code'], expected=True)

// after (tolerate non-JSON bodies)
try:
    error_code = self._parse_json(
        e.cause.read().decode(), episode_id)[0]['error_code']
except ExtractorError:
    error_code = '7plus returned HTTP 403'
raise ExtractorError(error_code, expected=True)
Defensive patterns

Strategy: try-catch

Try / catch

try:
    ie.extract(url)
except ExtractorError as e:
    if e.expected and 'GEO' in str(e).upper():
        # geo block: route via AU proxy or skip
        handle_geo_block(e)
    else:
        raise

Prevention

When it happens

Trigger: Calling _download_json on https://videoservice.swm.digital/playback with the shown query (appId=7plus, deliveryId=csai, referenceId=ref:<episode_id>) and receiving an HTTP 403 whose JSON body contains an 'error_code'. Typical causes: geo-blocked IP (7plus is Australia-only), expired/rotated episode referenceId, or the CDN rejecting the 'rule=' query rewrite.

Common situations: Running youtube-dl outside Australia for a 7plus URL; using a stale episode URL after SWM rotated content; a VPN endpoint blacklisted by the Brightcove/SWM edge; upstream schema change where the 403 body is HTML instead of JSON (then _parse_json itself fails).

Related errors


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