yt-dlp/yt-dlp · error · ExtractorError

Cannot find on-air {video_id} channel.

Error message

Cannot find on-air {video_id} channel.

What it means

For AbemaTV 'now-on-air' URLs the extractor downloads the live channel list from api.abema.io/v1/channels (with division=1 for news-global) and linearly matches video_id against each channel's id to grab its HLS playback URL. If the loop completes with no match, this expected ExtractorError is raised: the channel slug in your URL is not currently broadcasting (or not present in the API's response).

Source

Thrown at yt_dlp/extractor/abematv.py:372

            info['season_number'] = seri if seri < 100 else None
            # some anime like Detective Conan (though not available in AbemaTV)
            # has more than 1000 episodes (1026 as of 2021/11/15)
            info['episode_number'] = epis if epis < 2000 else None

        is_live, m3u8_url = False, None
        availability = 'public'
        if video_type == 'now-on-air':
            is_live = True
            channel_url = 'https://api.abema.io/v1/channels'
            if video_id == 'news-global':
                channel_url = update_url_query(channel_url, {'division': '1'})
            onair_channels = self._download_json(channel_url, video_id)
            for ch in onair_channels['channels']:
                if video_id == ch['id']:
                    m3u8_url = ch['playback']['hls']
                    break
            else:
                raise ExtractorError(f'Cannot find on-air {video_id} channel.', expected=True)
        elif video_type == 'episode':
            api_response = self._download_json(
                f'https://api.abema.io/v1/video/programs/{video_id}', video_id,
                note='Checking playability',
                headers=headers)
            if not traverse_obj(api_response, ('label', 'free', {bool})):
                # cannot acquire decryption key for these streams
                self.report_warning('This is a premium-only stream')
                availability = 'premium_only'
            info.update(traverse_obj(api_response, {
                'series': ('series', 'title'),
                'season': ('season', 'name'),
                'season_number': ('season', 'sequence'),
                'episode_number': ('episode', 'number'),
            }))
            if not title:
                title = traverse_obj(api_response, ('episode', 'title'))
            if not description:

View on GitHub (pinned to 81ecd58b13)

Solutions

  1. Pick a current live channel from https://abema.tv/now-on-air and use that fresh URL/slug
  2. Double-check the slug spelling against the site; news-global is the special-cased id for the news division
  3. If outside Japan, route through a JP VPN/proxy (or --proxy) — Abema geo-restricts its API responses
  4. Update yt-dlp in case the API contract changed, then retest with -v
Defensive patterns

Strategy: try-catch

Try / catch

from yt_dlp.utils import ExtractorError
try:
    info = ydl.extract_info(url, download=True)
except ExtractorError as e:
    if e.expected and 'Cannot find on-air' in str(e):
        logger.info('channel off-air or geo-blocked; refresh the now-on-air slug: %s', url)
    else:
        raise

Prevention

When it happens

Trigger: A URL like https://abema.tv/now-on-air/SLUG where SLUG is not among onair_channels['channels'][*]['id'] — e.g. the channel went off-air and Abema rotated slugs, a typo in the slug, or the API returned a reduced channel set because of geoblocking (Abema is Japan-only).

Common situations: Watching a time-limited live channel that ended; copied stale URLs; accessing Abema from outside Japan without a JP exit node so the channels payload differs; scheduled channels that have not started.

Related errors


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