encode/httpx · error · ValueError

Proxy keys should use proper URL forms rather than plain sch

Error message

Proxy keys should use proper URL forms rather than plain scheme strings. Instead of "{pattern}", use "{pattern}://"

What it means

httpx.URLPattern (used to match proxies and mounts) requires a proper URL-shaped pattern string. The constructor at httpx/_utils.py:165 checks if the pattern is non-empty and contains no ':' character; if so it raises ValueError telling you to turn a bare scheme like 'http' into a URL form like 'http://'. The validator exists because plain scheme strings would otherwise parse ambiguously or match nothing useful.

Source

Thrown at httpx/_utils.py:166

    True
    >>> pattern.matches(httpx.URL("http://example.com"))
    True
    >>> pattern.matches(httpx.URL("https://other.com"))
    False

    # With port matching...
    >>> pattern = URLPattern("https://example.com:1234")
    >>> pattern.matches(httpx.URL("https://example.com:1234"))
    True
    >>> pattern.matches(httpx.URL("https://example.com"))
    False
    """

    def __init__(self, pattern: str) -> None:
        from ._urls import URL

        if pattern and ":" not in pattern:
            raise ValueError(
                f"Proxy keys should use proper URL forms rather "
                f"than plain scheme strings. "
                f'Instead of "{pattern}", use "{pattern}://"'
            )

        url = URL(pattern)
        self.pattern = pattern
        self.scheme = "" if url.scheme == "all" else url.scheme
        self.host = "" if url.host == "*" else url.host
        self.port = url.port
        if not url.host or url.host == "*":
            self.host_regex: typing.Pattern[str] | None = None
        elif url.host.startswith("*."):
            # *.example.com should match "www.example.com", but not "example.com"
            domain = re.escape(url.host[2:])
            self.host_regex = re.compile(f"^.+\\.{domain}$")
        elif url.host.startswith("*"):
            # *example.com should match "www.example.com" and "example.com"

View on GitHub (pinned to b5addb64f0)

Solutions

  1. Suffix the bare scheme with '://': use 'http://', 'https://', or the wildcard 'all://' as the pattern/mount key.
  2. For env-driven config, normalize at the boundary: pattern = scheme if '://' in scheme or ':' in scheme else scheme + '://'.
  3. If passing a full URL pattern (e.g. 'https://example.com'), it already contains ':' and passes; only bare scheme strings fail.
  4. Double-check mounts/proxy dict keys when porting requests proxies={'http': ...} to httpx mounts={'http://': ...}; the key format changed.

Example fix

// before
client = httpx.Client(mounts={
    'http': httpx.HTTPTransport(proxy='http://proxy:8080'),  # raises ValueError
})

// after
client = httpx.Client(mounts={
    'http://': httpx.HTTPTransport(proxy='http://proxy:8080'),
})
Defensive patterns

Strategy: validation

Validate before calling

def normalize_proxy_key(pattern: str) -> str:
    """Ensure a proxy/mount key is a proper URL-shaped pattern.

    'http' -> 'http://', 'all' -> 'all://', full URLs and wildcard
    hosts pass through unchanged.
    """
    if not pattern:
        return pattern
    if ':' in pattern:
        return pattern
    return f'{pattern}://'

# Usage at the config boundary:
raw = {'http': transport, 'all': transport}
mounts = {normalize_proxy_key(k): v for k, v in raw.items()}

Type guard

def is_valid_urlpattern(pattern: str) -> bool:
    """True if pattern won't trip httpx.URLPattern's bare-scheme guard.

    Empty patterns are allowed by httpx; non-empty must contain ':'.
    """
    return pattern == '' or ':' in pattern

Try / catch

import httpx

def build_pattern(pattern: str) -> httpx.URLPattern:
    try:
        return httpx.URLPattern(pattern)
    except ValueError:
        # Bare scheme: retry with the URL form recommended by the error.
        if pattern and ':' not in pattern:
            return httpx.URLPattern(f'{pattern}://')
        raise

Prevention

When it happens

Trigger: Constructing httpx.URLPattern('http'), httpx.URLPattern('https'), or any non-empty string without a colon. Most commonly hit when configuring mounts/proxies with a dict whose keys are bare schemes: client = httpx.Client(mounts={'http': httpx.HTTPTransport(...)}) or Client(proxy=...) variants that route through URLPattern. The check fires before any URL parsing, so any colon-less non-empty pattern raises immediately.

Common situations: Configuring httpx proxies/mounts from examples that abbreviated the scheme, or from config files/env vars that store just 'http'/'https'/'all'. Migrating from requests-style proxies={'http': ...} (bare scheme keys are idiomatic there) to httpx mounts={'http://': ...}. Typos like 'httpto' or trailing tokens without '://'. Auto-generated patterns from a scheme list that forgot the '://' suffix.

Related errors


AI-assisted analysis of encode/httpx@b5addb64f0 (2026-08-04). Data as JSON: /data/errors/f04ec0516abe299e.json. Report an issue: GitHub.