{"record":{"id":"69021335aacc09a0","repo":"ytdl-org/youtube-dl","slug":"s-returned-error-s-690213","errorCode":null,"errorMessage":"%s returned error: %s","messagePattern":"(.+?) returned error: (.+?)","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"youtube_dl/extractor/tvigle.py","lineNumber":82,"sourceCode":"                (r'<div[^>]+class=[\"\\']player[\"\\'][^>]+id=[\"\\'](\\d+)',\n                 r'cloudId\\s*=\\s*[\"\\'](\\d+)',\n                 r'class=\"video-preview current_playing\" id=\"(\\d+)\"'),\n                webpage, 'video id')\n\n        video_data = self._download_json(\n            'http://cloud.tvigle.ru/api/play/video/%s/' % video_id, display_id)\n\n        item = video_data['playlist']['items'][0]\n\n        videos = item.get('videos')\n\n        error_message = item.get('errorMessage')\n        if not videos and error_message:\n            if item.get('isGeoBlocked') is True:\n                self.raise_geo_restricted(\n                    msg=error_message, countries=self._GEO_COUNTRIES)\n            else:\n                raise ExtractorError(\n                    '%s returned error: %s' % (self.IE_NAME, error_message),\n                    expected=True)\n\n        title = item['title']\n        description = item.get('description')\n        thumbnail = item.get('thumbnail')\n        duration = float_or_none(item.get('durationMilliseconds'), 1000)\n        age_limit = parse_age_limit(item.get('ageRestrictions'))\n\n        formats = []\n        for vcodec, url_or_fmts in item['videos'].items():\n            if vcodec == 'hls':\n                m3u8_url = url_or_none(url_or_fmts)\n                if not m3u8_url:\n                    continue\n                formats.extend(self._extract_m3u8_formats(\n                    m3u8_url, video_id, ext='mp4', entry_protocol='m3u8_native',\n                    m3u8_id='hls', fatal=False))","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/tvigle.py#L64-L100","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"import requests\nr = requests.get('http://cloud.tvigle.ru/api/play/video/%s/' % video_id)\nitem = r.json()['playlist']['items'][0]\nif not item.get('videos') and item.get('errorMessage'):\n    skip(video_id, reason=item['errorMessage'])","typeGuard":"def tvigle_unavailable(item: dict) -> bool:\n    return not item.get('videos') and bool(item.get('errorMessage'))","tryCatchPattern":"try:\n    ydl.extract_info(url)\nexcept ExtractorError as e:\n    if 'returned error:' in str(e) or 'geo' in str(e).lower():\n        mark_unavailable(url, str(e))\n    else:\n        raise","preventionTips":["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."],"tags":["extractor","tvigle","api-error","geo","availability"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}