ytdl-org/youtube-dl · error · ExtractorError
%s returned error: %s
Error message
%s returned error: %s
What it means
Raised by TvigleIE when the cloud.tvigle.ru API returns a playlist item with an errorMessage and no 'videos' dict, i.e. the server explicitly reported a playback error for that video id. Geo-blocked cases are routed separately to raise_geo_restricted; this branch covers all other server-side refusals (removed, restricted by age, etc.). Marked expected=True so it is surfaced as a normal failure, not a crash.
Source
Thrown at youtube_dl/extractor/tvigle.py:82
(r'<div[^>]+class=["\']player["\'][^>]+id=["\'](\d+)',
r'cloudId\s*=\s*["\'](\d+)',
r'class="video-preview current_playing" id="(\d+)"'),
webpage, 'video id')
video_data = self._download_json(
'http://cloud.tvigle.ru/api/play/video/%s/' % video_id, display_id)
item = video_data['playlist']['items'][0]
videos = item.get('videos')
error_message = item.get('errorMessage')
if not videos and error_message:
if item.get('isGeoBlocked') is True:
self.raise_geo_restricted(
msg=error_message, countries=self._GEO_COUNTRIES)
else:
raise ExtractorError(
'%s returned error: %s' % (self.IE_NAME, error_message),
expected=True)
title = item['title']
description = item.get('description')
thumbnail = item.get('thumbnail')
duration = float_or_none(item.get('durationMilliseconds'), 1000)
age_limit = parse_age_limit(item.get('ageRestrictions'))
formats = []
for vcodec, url_or_fmts in item['videos'].items():
if vcodec == 'hls':
m3u8_url = url_or_none(url_or_fmts)
if not m3u8_url:
continue
formats.extend(self._extract_m3u8_formats(
m3u8_url, video_id, ext='mp4', entry_protocol='m3u8_native',
m3u8_id='hls', fatal=False))View on GitHub (pinned to 956b8c5855)
Solutions
- Open the video page at tvigle.ru in a browser to confirm the video still exists and is playable in your region.
- If the page shows a regional restriction, use a VPN/Russian IP; the geo path is separate but the server may fold both into errorMessage.
- Find the video on the official tvigle.ru site and copy a fresh URL (old IDs may be dead).
- Update to a newer youtube-dl/yt-dlp build in case the API contract changed.
Defensive patterns
Strategy: try-catch
Validate before calling
import requests
r = requests.get('http://cloud.tvigle.ru/api/play/video/%s/' % video_id)
item = r.json()['playlist']['items'][0]
if not item.get('videos') and item.get('errorMessage'):
skip(video_id, reason=item['errorMessage']) Type guard
def tvigle_unavailable(item: dict) -> bool:
return not item.get('videos') and bool(item.get('errorMessage')) Try / catch
try:
ydl.extract_info(url)
except ExtractorError as e:
if 'returned error:' in str(e) or 'geo' in str(e).lower():
mark_unavailable(url, str(e))
else:
raise Prevention
- Probe the tvigle API JSON directly before download; it exposes errorMessage and isGeoBlocked.
- Keep per-URL try/except in crawlers so one dead video does not stop the batch.
When it happens
Trigger: GET http://cloud.tvigle.ru/api/play/video/<id>/ returns items[0] with errorMessage set (e.g. 'Video is deleted' or a Russian-language refusal) and item['videos'] empty/absent, while isGeoBlocked is not True.
Common situations: Video removed from tvigle.ru catalog, content taken down for rights reasons, region issues that the API does not flag as geo-block, stale video IDs from old links.
Related errors
- %s said: %s
- %s said: %s
- %s returned error: %s
- Episode %s is not yet available
- Episode %s is no longer available
AI-assisted analysis of ytdl-org/youtube-dl@956b8c5855 (2026-08-14).
Data as JSON: /api/errors/69021335aacc09a0.
Report an issue: GitHub.