{"record":{"id":"4e20d53dbc1c6391","repo":"ytdl-org/youtube-dl","slug":"s-says-s-ie-name-msg-or-video-unavailable","errorCode":null,"errorMessage":"%s says: %s (IE_NAME, msg) or 'Video unavailable: no reason found'","messagePattern":"(.+?) says: (.+?) \\(IE_NAME, msg\\) or 'Video unavailable: no reason found'","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"error","filePath":"youtube_dl/extractor/youporn.py","lineNumber":130,"sourceCode":"                    yield m.group('url')\n\n        return list(yield_urls())\n\n    def _real_extract(self, url):\n        # A different video ID (data-video-id) is hidden in the page but\n        # never seems to be used\n        video_id, display_id = self._match_valid_url(url).group('id', 'display_id')\n        url = 'http://www.youporn.com/watch/%s' % (video_id,)\n        webpage = self._download_webpage(\n            url, video_id, headers={'Cookie': 'age_verified=1'})\n\n        watchable = self._search_regex(\n            r'''(<div\\s[^>]*\\bid\\s*=\\s*('|\")?watch-container(?(2)\\2|(?!-)\\b)[^>]*>)''',\n            webpage, 'watchability', default=None)\n        if not watchable:\n            msg = re.split(r'\\s{4}', clean_html(get_element_by_id(\n                'mainContent', webpage)) or '')[0]\n            raise ExtractorError(\n                ('%s says: %s' % (self.IE_NAME, msg))\n                if msg else 'Video unavailable: no reason found',\n                expected=True)\n        # internal ID ?\n        # video_id = extract_attributes(watchable).get('data-video-id')\n\n        playervars = self._search_json(\n            r'\\bplayervars\\s*:', webpage, 'playervars', video_id)\n\n        def get_fmt(x):\n            v_url = url_or_none(x.get('videoUrl'))\n            if v_url:\n                x['videoUrl'] = v_url\n                return (x['format'], x)\n\n        defs_by_format = dict(traverse_obj(playervars, (\n            'mediaDefinitions', lambda _, v: v.get('format'), T(get_fmt))))\n","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/youporn.py#L112-L148","documentation":"Raised by YouPornIE._real_extract when the downloaded watch page contains no element with id 'watch-container' — i.e. the video is not watchable. The code then scrapes the #mainContent text, splits on runs of 4 spaces, and reports the first chunk as the site's reason (e.g. 'Video has been removed'), or the generic 'Video unavailable: no reason found' if nothing is scraped. expected=True frames it as a site condition.","triggerScenarios":"Extracting a youporn.com/watch/<id> URL (with Cookie age_verified=1) where the page lacks the watch-container div: removed videos, geo-blocked pages, or the generic 'video unavailable' template. The regex uses a backreference-conditional pattern so a renamed container id also fails to match.","commonSituations":"Dead/removed video links; region blocks serving a notice page instead of the player; age-gate behavior changing so the age_verified=1 cookie no longer unlocks the real page; template changes altering the container id or the 4-space-separated message layout in #mainContent.","solutions":["Open the same URL in a browser (incognito) and see what notice YouPorn shows — usually removal or region restriction; pick another source if so.","Confirm the request really sends Cookie: age_verified=1; without it the age-verify interstitial can hide the watch container.","If the video plays in a browser, dump the webpage and check whether the watch-container id was renamed; update the watchable regex or the get_element_by_id('mainContent') scraping accordingly.","For crawls, treat this expected error as a skip signal for dead entries rather than a hard failure."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"def youporn_watchable(html: str) -> bool:\n    return 'id=\"watch-container\"' in html or \"id='watch-container'\" in html","typeGuard":null,"tryCatchPattern":"try:\n    info = ydl.extract_info(url)\nexcept ExtractorError as e:\n    if str(e).startswith('YouPorn says:') or 'Video unavailable' in str(e):\n        mark_dead_and_skip(url)  # removal/geo/age-gate; read the embedded reason\n    else:\n        raise","preventionTips":["Always send Cookie: age_verified=1 (the extractor does; replicate it in custom clients).","Treat the scraped reason after 'says:' as the site's own notice and prune dead links from crawl lists."],"tags":["youporn","video-unavailable","html-scraping","expected-error"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}