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

  1. Skip non-video lectures in your script by catching this expected error per lecture.
  2. Filter the course curriculum JSON by asset_type == 'Video' before enqueueing lecture URLs.
  3. For article lectures, fetch the HTML body via Udemy's API or scrape the page directly.
  4. 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

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


AI-assisted analysis of ytdl-org/youtube-dl@956b8c5855 (2026-08-14). Data as JSON: /api/errors/3f37db24a31364e3. Report an issue: GitHub.