yt-dlp/yt-dlp · error · ExtractorError

Got empty response from playback API

Error message

Got empty response from playback API

What it means

OnDemandKoreaIE calls odkmedia.io's v3 playback API with 403/404 whitelisted via expected_status, so HTTP errors come back as JSON. When the response has no 'result' object it surfaces the API's own message (messages.__default or title) or falls back to 'Got empty response from playback API', always with expected=True. The generic text appears only when the error envelope carries no readable message; the usual causes are regional licensing (403) or removed/wrong contentId (404).

Source

Thrown at yt_dlp/extractor/ondemandkorea.py:80

            'duration': 7267.0,
            'title': 'The Outlaws: Main Movie',
            'thumbnail': r're:^https?://.*\.(jpg|jpeg|png)',
            'age_limit': 18,
        },
    }, {
        'url': 'https://www.ondemandkorea.com/en/player/vod/capture-the-moment-how-is-that-possible?contentId=1605006',
        'only_matching': True,
    }]

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

        data = self._download_json(
            f'https://odkmedia.io/odx/api/v3/playback/{video_id}/', video_id, fatal=False,
            headers={'service-name': 'odk'}, query={'did': str(uuid.uuid4())}, expected_status=(403, 404))
        if not traverse_obj(data, ('result', {dict})):
            msg = traverse_obj(data, ('messages', '__default'), 'title', expected_type=str)
            raise ExtractorError(msg or 'Got empty response from playback API', expected=True)

        data = data['result']

        def try_geo_bypass(url):
            return traverse_obj(url, ({parse_qs}, 'stream_url', 0, {url_or_none})) or url

        formats = []
        for m3u8_url in traverse_obj(data, (('sources', 'manifest'), ..., 'url', {url_or_none}, {try_geo_bypass})):
            mod_url = re.sub(r'_720(p?)\.m3u8', r'_1080\1.m3u8', m3u8_url)
            if mod_url != m3u8_url:
                mod_format = self._extract_m3u8_formats(
                    mod_url, video_id, note='Checking for higher quality format',
                    errnote='No higher quality format found', fatal=False)
                if mod_format:
                    formats.extend(mod_format)
                    continue
            formats.extend(self._extract_m3u8_formats(m3u8_url, video_id, fatal=False))

View on GitHub (pinned to 81ecd58b13)

Solutions

  1. Read the full error line first — when present, the API's own message (messages.__default) states the real reason (region vs removed).
  2. Route through an egress IP in a licensed region if you are entitled to the content.
  3. Re-resolve the contentId from the current OnDemandKorea page URL; stale ids return empty results.
  4. Confirm the title still exists on the site; if yes and it still fails on latest yt-dlp, report with -v.

Example fix

# before — stale contentId / wrong region
yt-dlp 'https://www.ondemandkorea.com/en/player/vod/some-title?contentId=1605006'

# after — refresh contentId from the site and run from a licensed region
yt-dlp --proxy 'http://us-proxy.example:8080' 'https://www.ondemandkorea.com/en/player/vod/some-title?contentId=<current-id>'
Defensive patterns

Strategy: try-catch

Type guard

from yt_dlp.utils import DownloadError

def is_odk_playback_empty(e: BaseException) -> bool:
    return isinstance(e, DownloadError) and 'Got empty response from playback API' in str(e)

Try / catch

from yt_dlp import YoutubeDL
from yt_dlp.utils import DownloadError

try:
    with YoutubeDL({'quiet': True}) as ydl:
        ydl.extract_info(url, download=True)
except DownloadError as e:
    if 'Got empty response from playback API' in str(e):
        refresh_content_id_and_egress(url)  # geo or removed: change egress / re-resolve id, retry once
    else:
        raise

Prevention

When it happens

Trigger: The playback API answers 403 (region not licensed for the title), 404 (removed or stale contentId), or 200 with an error envelope lacking messages.__default/title — the last case produces exactly the generic message.

Common situations: Viewers outside ODK's licensed regions without a matching egress IP; deep links with stale contentIds after catalog reshuffles; transient ODK API incidents.

Related errors


AI-assisted analysis of yt-dlp/yt-dlp@81ecd58b13 (2026-08-22). Data as JSON: /api/errors/dd8bd181d7315e1b. Report an issue: GitHub.