psf/requests · error · InvalidProxyURL

Please check proxy URL. It is malformed and could be…

Error message

Please check proxy URL. It is malformed and could be missing the host.

What it means

Raised when a proxy is selected for the request but parse_url(proxy) yields no host component. The code prepends an http:// scheme if missing, parses the result, and rejects it as an InvalidProxyURL if proxy_url.host is falsy. This guards against proxy strings like "://", "http://", or bare credentials with no host before any connection is attempted.

Solutions

  1. Validate the proxy string has a host before assigning it: parse with urllib3.util.parse_url and check .host.
  2. Clear empty proxy env vars (unset HTTP_PROXY when not needed) rather than leaving them blank.
  3. Normalize proxy config through a single helper that rejects malformed entries at startup.
  4. For per-request bypass, set proxies={'http': None, 'https': None} rather than an empty string.
  5. Log the resolved proxy at DEBUG in your session wrapper so malformed values are visible.

Example fix

# before
proxies = {"https": "http://"}  # -> InvalidProxyURL
requests.get("https://example.com", proxies=proxies)

# after
from urllib3.util import parse_url
p = "http://proxy.local:8080"
assert parse_url(p).host, f"bad proxy: {p}"
requests.get("https://example.com", proxies={"https": p})
Defensive patterns

Strategy: validation

Validate before calling

from urllib3.util import parse_url

def normalize_proxies(proxies: dict | None) -> dict | None:
    """Drop/fix proxy entries whose parsed host is empty."""
    if not proxies:
        return proxies
    out = {}
    for scheme, proxy in proxies.items():
        if proxy is None:
            out[scheme] = None  # explicit bypass
            continue
        if not parse_url(proxy).host:
            raise ValueError(f"proxy for {scheme} has no host: {proxy!r}")
        out[scheme] = proxy
    return out

requests.get(url, proxies=normalize_proxies(proxies))

Type guard

from urllib3.util import parse_url

def is_well_formed_proxy(proxy) -> bool:
    if proxy is None:
        return True
    if not isinstance(proxy, str) or not proxy:
        return False
    return bool(parse_url(proxy).host)

Try / catch

import requests.exceptions as exc

try:
    resp = session.get(url, proxies=proxies)
except exc.InvalidProxyURL as e:
    raise ValueError(
        f"misconfigured proxy {proxies!r}: missing host"
    ) from e

Prevention

When it happens

Trigger: Triggered inside send (the modern path) when proxies maps a scheme to a proxy string that parses to an empty host, e.g. proxies={'http': ''}, {'http': 'http://'}, {'https': ':8080'}, or a malformed value from HTTP_PROXY env var. The check runs only when select_proxy returns a non-empty proxy.

Common situations: Seen when HTTP_PROXY/HTTPS_PROXY env vars are set to empty or malformed values in CI, when a config file writes a proxy with a typo, when an empty proxy string is intentionally used to bypass but is malformed, or when proxy parsing of a socks URL occurs without the socks extra (though that path raises InvalidSchema separately).

Understand the failure class

Related errors


AI-assisted analysis of psf/requests@8068356288 (2026-08-11). Data as JSON: /api/errors/825b20b7267f0bd1. Report an issue: GitHub.

Appendix: source

Thrown at src/requests/adapters.py:496

        :rtype:
            urllib3.HTTPConnectionPool
        """
        assert _is_prepared(request)

        proxy = select_proxy(request.url, proxies)
        try:
            host_params, pool_kwargs = self.build_connection_pool_key_attributes(
                request,
                verify,
                cert,
            )
        except ValueError as e:
            raise InvalidURL(e, request=request)
        if proxy:
            proxy = prepend_scheme_if_needed(proxy, "http")
            proxy_url = parse_url(proxy)
            if not proxy_url.host:
                raise InvalidProxyURL(
                    "Please check proxy URL. It is malformed "
                    "and could be missing the host."
                )
            proxy_manager = self.proxy_manager_for(proxy)
            conn = proxy_manager.connection_from_host(
                **host_params, pool_kwargs=pool_kwargs
            )
        else:
            # Only scheme should be lower case
            conn = self.poolmanager.connection_from_host(
                **host_params, pool_kwargs=pool_kwargs
            )

        return conn

    def get_connection(
        self, url: str, proxies: dict[str, str] | None = None
    ) -> HTTPConnectionPool:

View on GitHub (pinned to 8068356288)