ytdl-org/youtube-dl · warning · ExtractorError
Lecture %s is not a video
Error message
Lecture %s is not a video
What it means
Raised by UdemyIE._real_extract when the lecture's asset type is not 'Video' — e.g. 'Article', 'Quiz', 'Presentation', or 'Pdf' assets have no media stream to extract. Expected error identifying the lecture id; it protects the downstream code that assumes stream_url/media assets.
Source
Thrown at youtube_dl/extractor/udemy.py:238
try:
lecture = self._download_lecture(course_id, lecture_id)
except ExtractorError as e:
# Error could possibly mean we are not enrolled in the course
if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403:
self._enroll_course(url, webpage, course_id)
lecture = self._download_lecture(course_id, lecture_id)
else:
raise
title = lecture['title']
description = lecture.get('description')
asset = lecture['asset']
asset_type = asset.get('asset_type') or asset.get('assetType')
if asset_type != 'Video':
raise ExtractorError(
'Lecture %s is not a video' % lecture_id, expected=True)
stream_url = asset.get('stream_url') or asset.get('streamUrl')
if stream_url:
youtube_url = self._search_regex(
r'(https?://www\.youtube\.com/watch\?v=.*)', stream_url, 'youtube URL', default=None)
if youtube_url:
return self.url_result(youtube_url, 'Youtube')
video_id = compat_str(asset['id'])
thumbnail = asset.get('thumbnail_url') or asset.get('thumbnailUrl')
duration = float_or_none(asset.get('data', {}).get('duration'))
subtitles = {}
automatic_captions = {}
formats = []
View on GitHub (pinned to 956b8c5855)
Solutions
- Skip non-video lectures in your script by catching this expected error per lecture.
- Filter the course curriculum JSON by asset_type == 'Video' before enqueueing lecture URLs.
- For article lectures, fetch the HTML body via Udemy's API or scrape the page directly.
- Handle attachments separately if your tool supports them (yt-dlp marks these lectures with _type 'url' style fallbacks in some versions).
Defensive patterns
Strategy: validation
Validate before calling
curriculum = udemy_api_get('/courses/%s/subscriber-curriculum-items/' % course_id)
for item in curriculum['results']:
asset = item.get('asset') or {}
if (asset.get('asset_type') or asset.get('assetType')) != 'Video':
continue # skip articles/quizzes before extracting Type guard
def is_video_lecture(lecture: dict) -> bool:
asset = lecture.get('asset') or {}
return (asset.get('asset_type') or asset.get('assetType')) == 'Video' Try / catch
for lecture_url in course_lecture_urls:
try:
ydl.extract_info(lecture_url)
except ExtractorError as e:
if 'is not a video' in str(e):
continue # article/quiz lecture — expected in mixed courses
else:
raise Prevention
- Filter the curriculum JSON by asset_type == 'Video' before enqueueing lecture URLs.
- Treat this expected error as a skip signal, not a failure, in course-mirror scripts.
When it happens
Trigger: Extracting a lecture URL whose lecture['asset']['asset_type'] (or assetType) is anything other than the literal 'Video' — article lectures, quizzes, coding exercises, downloadable attachments.
Common situations: Batch-downloading a whole course where most sections mix video lectures with reading assignments and quizzes; curriculum restructured to articles.
Related errors
- No chapters found for course %s
- There's no video in this tweet.
- Course %s is not free. You have to pay for it before you can
- Udemy asks you to solve a CAPTCHA. Login with browser, solve
- Unable to login: %s
AI-assisted analysis of ytdl-org/youtube-dl@956b8c5855 (2026-08-14).
Data as JSON: /api/errors/3f37db24a31364e3.
Report an issue: GitHub.