{"record":{"id":"90b46f53e96ebfa5","repo":"ytdl-org/youtube-dl","slug":"the-given-url-does-not-contain-a-video","errorCode":null,"errorMessage":"The given url does not contain a video","messagePattern":"The given url does not contain a video","errorType":"exception","errorClass":"ExtractorError","httpStatus":null,"severity":"warning","filePath":"youtube_dl/extractor/ninegag.py","lineNumber":47,"sourceCode":"            'like_count': int,\n            'dislike_count': int,\n            'comment_count': int,\n        }\n    }, {\n        # HTML escaped title\n        'url': 'https://9gag.com/gag/av5nvyb',\n        'only_matching': True,\n    }]\n\n    def _real_extract(self, url):\n        post_id = self._match_id(url)\n        post = self._download_json(\n            'https://9gag.com/v1/post', post_id, query={\n                'id': post_id\n            })['data']['post']\n\n        if post.get('type') != 'Animated':\n            raise ExtractorError(\n                'The given url does not contain a video',\n                expected=True)\n\n        title = unescapeHTML(post['title'])\n\n        duration = None\n        formats = []\n        thumbnails = []\n        for key, image in (post.get('images') or {}).items():\n            image_url = url_or_none(image.get('url'))\n            if not image_url:\n                continue\n            ext = determine_ext(image_url)\n            image_id = key.strip('image')\n            common = {\n                'url': image_url,\n                'width': int_or_none(image.get('width')),\n                'height': int_or_none(image.get('height')),","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/ytdl-org/youtube-dl/blob/956b8c585591b401a543e409accb163eeaaa1193/youtube_dl/extractor/ninegag.py#L29-L65","documentation":"Raised by NineGagIE when the post fetched from 9gag's v1/post API has type != 'Animated' — i.e. the URL points to a static image, article, or other non-video post. Only 'Animated' posts carry media streams. Marked expected=True, it cleanly rejects non-video permalinks.","triggerScenarios":"Calling the extractor on a 9gag.com/gag/<id> URL whose post['type'] is 'Photo', 'Article', etc. instead of 'Animated'.","commonSituations":"Feeding scraped 9gag permalinks indiscriminately (most are images); GIF/photo posts shared as if videos; automation that assumes every gag is downloadable video.","solutions":["Filter by post type before downloading: query the same https://9gag.com/v1/post?id=<id> endpoint and keep only type == 'Animated'.","For photo posts, fetch post['images']['image700']['url'] (or similar) directly — no video extractor needed.","Catch this expected ExtractorError in batch scripts and skip non-video items."],"exampleFix":"# before\nurls.each { |u| system(\"youtube-dl #{u}\") }\n\n# after (pre-filter via the public JSON API)\nimport requests, sys\nfor u in urls:\n    pid = u.rstrip('/').rsplit('/', 1)[-1]\n    p = requests.get('https://9gag.com/v1/post', params={'id': pid}).json()['data']['post']\n    if p.get('type') == 'Animated':\n        sys.argv = ['youtube-dl', u]  # download video\n# else: fetch image URL from p['images']","handlingStrategy":"validation","validationCode":"import requests\np = requests.get('https://9gag.com/v1/post', params={'id': post_id}).json()['data']['post']\nassert p.get('type') == 'Animated', f\"post is {p.get('type')}, not a video\"","typeGuard":null,"tryCatchPattern":"except ExtractorError as e:\n    if e.expected and 'does not contain a video' in str(e):\n        image_url = fetch_image_fallback(post_id)  # photo posts are still fetchable\n        if image_url:\n            download(image_url)\n    else:\n        raise","preventionTips":["Pre-filter permalinks through the v1/post JSON API and keep only type == 'Animated'.","Handle photo posts by fetching the image URL directly from post['images'].","Log non-video skips separately — they are expected, not errors."],"tags":["content-type","validation","extractor","expected-error"],"backgroundTag":null,"analyzedSha":"956b8c585591b401a543e409accb163eeaaa1193","analyzedAt":"2026-08-14T18:59:47.863Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}