ytdl-org/youtube-dl · warning · ExtractorError

%s is offline

Error message

%s is offline

What it means

Raised by TwitchStreamIE when the channel's user object exists but user.stream is falsy — the channel is simply not broadcasting right now. Expected error; it is a state condition, not a failure to parse.

Source

Thrown at youtube_dl/extractor/twitch.py:796

                    'isVodOrCollection': False,
                    'vodID': '',
                },
            }, {
                'operationName': 'VideoPreviewOverlay',
                'variables': {'login': channel_name},
            }],
            'Downloading stream GraphQL')

        user = gql[0]['data']['user']

        if not user:
            raise ExtractorError(
                '%s does not exist' % channel_name, expected=True)

        stream = user['stream']

        if not stream:
            raise ExtractorError('%s is offline' % channel_name, expected=True)

        access_token = self._download_access_token(
            channel_name, 'stream', 'channelName')
        token = access_token['value']

        stream_id = stream.get('id') or channel_name
        query = {
            'allow_source': 'true',
            'allow_audio_only': 'true',
            'allow_spectre': 'true',
            'p': random.randint(1000000, 10000000),
            'player': 'twitchweb',
            'playlist_include_framerate': 'true',
            'segment_preference': '4',
            'sig': access_token['signature'].encode('utf-8'),
            'token': token.encode('utf-8'),
        }
        formats = self._extract_m3u8_formats(

View on GitHub (pinned to 956b8c5855)

Solutions

  1. Check the channel page for a LIVE badge; if offline, wait for the broadcast and retry.
  2. Download the VOD afterwards via its /videos/ URL once published.
  3. Use a monitoring script/timer to re-attempt when the stream comes online.
  4. If it is a rerun, try the underlying VOD URL instead.
Defensive patterns

Strategy: retry

Validate before calling

import requests
q = [{'operationName': 'StreamMetadata', 'variables': {'login': channel}}]
r = requests.post('https://gql.twitch.tv/gql', json=q, headers={'Client-ID': CLIENT_ID})
stream = (r.json()[0]['data'].get('user') or {}).get('stream')
if not stream:
    schedule_retry(channel, when='next broadcast window')

Type guard

def is_streaming(data: dict) -> bool:
    return bool((data.get('user') or {}).get('stream'))

Try / catch

try:
    ydl.extract_info(channel_url)
except ExtractorError as e:
    if 'is offline' in str(e):
        retry_later(channel_url, backoff_minutes=15)  # state, not failure
    else:
        raise

Prevention

When it happens

Trigger: Extracting any twitch.tv/<name> URL for a channel that is currently offline (user['stream'] null/empty after the existence check passed).

Common situations: Trying to download a live stream after it ended, scheduled streams, reruns the API does not expose as stream, timezone mistakes about broadcast schedules.

Related errors


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