yt-dlp/yt-dlp · error · ExtractorError
Stream is offline
Error message
Stream is offline
What it means
PicartoIE (yt_dlp/extractor/picarto.py:53) extracts LIVE streams from picarto.tv channels. It queries the Picarto GraphQL API at https://ptvintern.picarto.tv/ptvapi for channel metadata, and if the returned channel.online field equals 0 the channel is not broadcasting, so extraction stops immediately with this expected error. There is no live feed to download in this state; this code path never returns formats for an offline channel.
Source
Thrown at yt_dlp/extractor/picarto.py:53
data = self._download_json(
'https://ptvintern.picarto.tv/ptvapi', channel_id, query={
'query': '''{
channel(name: "%s") {
adult
id
online
stream_name
title
}
getLoadBalancerUrl(channel_name: "%s") {
url
}
}''' % (channel_id, channel_id), # noqa: UP031
}, headers={'Accept': '*/*', 'Content-Type': 'application/json'})['data']
metadata = data['channel']
if metadata.get('online') == 0:
raise ExtractorError('Stream is offline', expected=True)
cdn_data = self._download_json(''.join((
update_url(data['getLoadBalancerUrl']['url'], scheme='https'),
'/stream/json_', metadata['stream_name'], '.js')),
channel_id, 'Downloading load balancing info')
formats = []
for source in (cdn_data.get('source') or []):
source_url = source.get('url')
if not source_url:
continue
source_type = source.get('type')
if source_type == 'html5/application/vnd.apple.mpegurl':
formats.extend(self._extract_m3u8_formats(
source_url, channel_id, 'mp4', m3u8_id='hls', fatal=False))
elif source_type == 'html5/video/mp4':
formats.append({
'url': source_url,View on GitHub (pinned to 81ecd58b13)
Solutions
- Open https://picarto.tv/<channel> in a browser and confirm the channel is actually live before downloading.
- If you want past broadcasts, use a VOD URL handled by PicartoVodIE (e.g. https://picarto.tv/<channel>/videos/<id> or https://picarto.tv/videopopout/<file>) instead of the live channel URL.
- If you automate, poll the same GraphQL field (channel(name: ...) { online }) yourself and only invoke yt-dlp when it is 1.
- Note yt-dlp's own test for this extractor is marked skip: 'Stream is offline' — hitting it is normal behaviour, not a bug.
Example fix
# before: live-channel extraction while the streamer is offline yt-dlp https://picarto.tv/SomeChannel # after: fetch a recording via the VOD extractor instead yt-dlp https://picarto.tv/SomeChannel/videos/771008
Defensive patterns
Strategy: validation
Validate before calling
import requests
def picarto_is_live(channel_id):
query = '{ channel(name: "%s") { online } }' % channel_id
res = requests.get(
'https://ptvintern.picarto.tv/ptvapi',
params={'query': query},
headers={'Accept': '*/*', 'Content-Type': 'application/json'},
timeout=10).json()
return res['data']['channel']['online'] == 1
# only hand the URL to yt-dlp when the channel is actually broadcasting
if picarto_is_live('SomeChannel'):
... # run YoutubeDL here Type guard
from yt_dlp.utils import ExtractorError
def is_stream_offline(e: BaseException) -> bool:
"""True when e is the expected Picarto 'Stream is offline' failure."""
return isinstance(e, ExtractorError) and e.expected and 'Stream is offline' in str(e) Try / catch
from yt_dlp.utils import ExtractorError
try:
info = ydl.extract_info('https://picarto.tv/SomeChannel', download=True)
except ExtractorError as e:
if e.expected and 'Stream is offline' in str(e):
logger.info('channel not broadcasting; skipping')
else:
raise Prevention
- Poll channel.online via the same GraphQL query the extractor uses before invoking yt-dlp.
- For archives, always use /videos/<id> or /videopopout/ URLs (PicartoVodIE) instead of the live channel URL.
- In watcher scripts, treat this expected error as a normal state, not an alert-worthy failure.
When it happens
Trigger: Running yt-dlp on a https://picarto.tv/<channel> URL (matched by PicartoIE, which defers to PicartoVodIE for /videos/ and /videopopout/ URLs) at a moment when the GraphQL response has channel.online == 0, i.e. the streamer is offline. Automated channel watchers typically hit this constantly.
Common situations: Monitoring scripts polling channels that are rarely live; following a scheduled-stream announcement page before the stream starts; a channel that was renamed so the slug points at a dead (offline) channel.
Related errors
- Streamer is not live
- Mirrativ says: {error_message}
- Neither archive nor live is available.
- The channel is not currently live
- Stream is offline.
AI-assisted analysis of yt-dlp/yt-dlp@81ecd58b13 (2026-08-22).
Data as JSON: /api/errors/a4e327100c72f523.
Report an issue: GitHub.