psf/requests · error · MissingSchema
Invalid URL : No scheme supplied. Perhaps you meant…
Error message
Invalid URL {url!r}: No scheme supplied. Perhaps you meant https://{url}? What it means
Raised by PreparedRequest.prepare_url when parse_url succeeded but returned an empty scheme. Every HTTP(S) request needs a scheme to route through the correct adapter; a URL like 'example.com/path' parses but has no scheme, so requests raises MissingSchema and suggests the most likely fix (prepending 'https://'). This catches the common 'forgot the protocol' mistake.
Solutions
- Prefix the URL with a scheme: requests.get('https://' + url) if you know it should be HTTPS, or normalize inputs upstream.
- Validate with urllib.parse.urlparse and require a non-empty .scheme before issuing the request.
- For 'localhost'-style shortcuts, use the full form 'http://localhost:8000'.
Example fix
// before
requests.get('example.com/api')
// after
requests.get('https://example.com/api') Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlparse
def ensure_scheme(url: str, default: str = 'https') -> str:
if '://' not in url:
url = f'{default}://{url}'
return url Type guard
from urllib.parse import urlparse
def has_scheme(url: str) -> bool:
return bool(urlparse(url).scheme) Try / catch
from requests.exceptions import MissingSchema
try:
resp = requests.get(url)
except MissingSchema:
resp = requests.get('https://' + url) Prevention
- Always include the scheme ('https://' or 'http://') when constructing URLs.
- Normalize user-supplied domains by prepending a default scheme.
- Validate .scheme is non-empty via urlparse before calling requests.
When it happens
Trigger: Calling requests.get('example.com/api') (no scheme); building URLs by concatenating a host without a protocol prefix; receiving a bare hostname from config and passing it straight to requests.
Common situations: User-supplied 'domains' instead of full URLs; configs that store hosts separately from schemes; copy-pasting a URL that lost its 'https://' when the browser stripped it.
Related errors
- {e.args}
- Invalid URL : No host supplied
- {e}
- Auth hooks must be callable.
- Cookie headers should be added with…
AI-assisted analysis of psf/requests@8068356288 (2026-08-11).
Data as JSON: /api/errors/11675c841b2d515d.
Report an issue: GitHub.
Appendix: source
Thrown at src/requests/models.py:516
# Remove leading whitespaces from url
url = url.lstrip()
# Don't do any URL preparation for non-HTTP schemes like `mailto`,
# `data` etc to work around exceptions from `url_parse`, which
# handles RFC 3986 only.
if ":" in url and not url.lower().startswith("http"):
self.url = url
return
# Support for unicode domain names and paths.
try:
scheme, auth, host, port, path, query, fragment = parse_url(url)
except LocationParseError as e:
raise InvalidURL(*e.args)
if not scheme:
raise MissingSchema(
f"Invalid URL {url!r}: No scheme supplied. "
f"Perhaps you meant https://{url}?"
)
if not host:
raise InvalidURL(f"Invalid URL {url!r}: No host supplied")
# In general, we want to try IDNA encoding the hostname if the string contains
# non-ASCII characters. This allows users to automatically get the correct IDNA
# behaviour. For strings containing only ASCII characters, we need to also verify
# it doesn't start with a wildcard (*), before allowing the unencoded hostname.
if not unicode_is_ascii(host):
try:
host = self._get_idna_encoded_host(host)
except UnicodeError:
raise InvalidURL("URL has an invalid label.")
elif host.startswith(("*", ".")):
raise InvalidURL("URL has an invalid label.")View on GitHub (pinned to 8068356288)