ytdl-org/youtube-dl · warning · ExtractorError

%r is not a valid URL. Set --default-search "ytsearch" (or r

Error message

%r is not a valid URL. Set --default-search "ytsearch" (or run  youtube-dl "ytsearch:%s" ) to search YouTube

What it means

Raised by the generic extractor when the URL has no scheme, the input does not look like a bare domain (fails ^[^\s/]+\.[^\s/]+/), and default_search is 'error' or the default 'fixup_error'. It tells the user to either supply a full URL or opt into YouTube search. Marked expected=True.

Source

Thrown at youtube_dl/extractor/generic.py:2555

                default_search = 'fixup_error'

            if default_search in ('auto', 'auto_warning', 'fixup_error'):
                if re.match(r'^[^\s/]+\.[^\s/]+/', url):
                    self._downloader.report_warning('The url doesn\'t specify the protocol, trying with http')
                    return self.url_result('http://' + url)
                elif default_search != 'fixup_error':
                    if default_search == 'auto_warning':
                        if re.match(r'^(?:url|URL)$', url):
                            raise ExtractorError(
                                'Invalid URL:  %r . Call youtube-dl like this:  youtube-dl -v "https://www.youtube.com/watch?v=BaW_jenozKc"  ' % url,
                                expected=True)
                        else:
                            self._downloader.report_warning(
                                'Falling back to youtube search for  %s . Set --default-search "auto" to suppress this warning.' % url)
                    return self.url_result('ytsearch:' + url)

            if default_search in ('error', 'fixup_error'):
                raise ExtractorError(
                    '%r is not a valid URL. '
                    'Set --default-search "ytsearch" (or run  youtube-dl "ytsearch:%s" ) to search YouTube'
                    % (url, url), expected=True)
            else:
                if ':' not in default_search:
                    default_search += ':'
                return self.url_result(default_search + url)

        url, smuggled_data = unsmuggle_url(url)
        force_videoid = None
        is_intentional = smuggled_data and smuggled_data.get('to_generic')
        if smuggled_data and 'force_videoid' in smuggled_data:
            force_videoid = smuggled_data['force_videoid']
            video_id = force_videoid
        else:
            video_id = self._generic_id(url)

        self.to_screen('%s: Requesting header' % video_id)

View on GitHub (pinned to 956b8c5855)

Solutions

  1. Supply the full URL with scheme: youtube-dl "https://www.youtube.com/watch?v=BaW_jenozKc"
  2. Or enable search: youtube-dl --default-search ytsearch BaW_jenozKc
  3. Or use the ytsearch prefix directly: youtube-dl "ytsearch:BaW_jenozKc"

Example fix

# before
youtube-dl BaW_jenozKc
# after
youtube-dl "ytsearch:BaW_jenozKc"
# or
youtube-dl "https://www.youtube.com/watch?v=BaW_jenozKc"
Defensive patterns

Strategy: validation

Validate before calling

import re
def needs_scheme(u):
    return not re.match(r'^[a-zA-Z][a-zA-Z0-9+.-]*://', u)
if needs_scheme(arg):
    arg = 'https://' + arg  # or reject, or prefix ytsearch:

Type guard

def is_full_url(u):
    return bool(re.match(r'^https?://\S+$', u))

Try / catch

try:
    ydl.download([arg])
except DownloadError as e:
    if 'not a valid URL' in str(e):
        arg = 'ytsearch:' + arg  # or fix the URL and retry
    else:
        raise

Prevention

When it happens

Trigger: Passing a scheme-less argument that is not a domain path, e.g. 'youtube-dl BaW_jenozKc' or 'youtube-dl some random text', with default --default-search fixup_error (the default when unset) or explicit 'error'.

Common situations: Users pasting video IDs or titles instead of URLs, missing https:// prefix on copied links, or scripts passing unquoted/unset variables.

Related errors


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