ytdl-org/youtube-dl · warning · ExtractorError
The given url does not contain a video
Error message
The given url does not contain a video
What it means
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.
Source
Thrown at youtube_dl/extractor/ninegag.py:47
'like_count': int,
'dislike_count': int,
'comment_count': int,
}
}, {
# HTML escaped title
'url': 'https://9gag.com/gag/av5nvyb',
'only_matching': True,
}]
def _real_extract(self, url):
post_id = self._match_id(url)
post = self._download_json(
'https://9gag.com/v1/post', post_id, query={
'id': post_id
})['data']['post']
if post.get('type') != 'Animated':
raise ExtractorError(
'The given url does not contain a video',
expected=True)
title = unescapeHTML(post['title'])
duration = None
formats = []
thumbnails = []
for key, image in (post.get('images') or {}).items():
image_url = url_or_none(image.get('url'))
if not image_url:
continue
ext = determine_ext(image_url)
image_id = key.strip('image')
common = {
'url': image_url,
'width': int_or_none(image.get('width')),
'height': int_or_none(image.get('height')),View on GitHub (pinned to 956b8c5855)
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.
Example fix
# before
urls.each { |u| system("youtube-dl #{u}") }
# after (pre-filter via the public JSON API)
import requests, sys
for u in urls:
pid = u.rstrip('/').rsplit('/', 1)[-1]
p = requests.get('https://9gag.com/v1/post', params={'id': pid}).json()['data']['post']
if p.get('type') == 'Animated':
sys.argv = ['youtube-dl', u] # download video
# else: fetch image URL from p['images'] Defensive patterns
Strategy: validation
Validate before calling
import requests
p = requests.get('https://9gag.com/v1/post', params={'id': post_id}).json()['data']['post']
assert p.get('type') == 'Animated', f"post is {p.get('type')}, not a video" Try / catch
except ExtractorError as e:
if e.expected and 'does not contain a video' in str(e):
image_url = fetch_image_fallback(post_id) # photo posts are still fetchable
if image_url:
download(image_url)
else:
raise Prevention
- 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.
When it happens
Trigger: Calling the extractor on a 9gag.com/gag/<id> URL whose post['type'] is 'Photo', 'Article', etc. instead of 'Animated'.
Common situations: Feeding scraped 9gag permalinks indiscriminately (most are images); GIF/photo posts shared as if videos; automation that assumes every gag is downloadable video.
Related errors
AI-assisted analysis of ytdl-org/youtube-dl@956b8c5855 (2026-08-14).
Data as JSON: /api/errors/90b46f53e96ebfa5.
Report an issue: GitHub.