encode/httpx · error · InvalidURL
Invalid port: {port!r}
Error message
Invalid port: {port!r} What it means
Raised by normalize_port when the port string cannot be converted with int(port). Ports must be decimal integers; non-numeric strings (or floats-as-strings) are rejected as InvalidURL. Note a numeric-but-out-of-range port is NOT caught here (that surfaces from the OS at connect time).
Source
Thrown at httpx/_urlparse.py:411
def normalize_port(port: str | int | None, scheme: str) -> int | None:
# From https://tools.ietf.org/html/rfc3986#section-3.2.3
#
# "A scheme may define a default port. For example, the "http" scheme
# defines a default port of "80", corresponding to its reserved TCP
# port number. The type of port designated by the port number (e.g.,
# TCP, UDP, SCTP) is defined by the URI scheme. URI producers and
# normalizers should omit the port component and its ":" delimiter if
# port is empty or if its value would be the same as that of the
# scheme's default."
if port is None or port == "":
return None
try:
port_as_int = int(port)
except ValueError:
raise InvalidURL(f"Invalid port: {port!r}")
# See https://url.spec.whatwg.org/#url-miscellaneous
default_port = {"ftp": 21, "http": 80, "https": 443, "ws": 80, "wss": 443}.get(
scheme
)
if port_as_int == default_port:
return None
return port_as_int
def validate_path(path: str, has_scheme: bool, has_authority: bool) -> None:
"""
Path validation rules that depend on if the URL contains
a scheme or authority component.
See https://datatracker.ietf.org/doc/html/rfc3986.html#section-3.3
"""
if has_authority:View on GitHub (pinned to b5addb64f0)
Solutions
- Coerce the port to int explicitly before passing: port = int(port_str.strip()).
- Validate the string with .isdecimal() and bounds 0 <= port <= 65535.
- Strip whitespace/newlines from config-derived port strings.
- Pass port as an int (the kwargs API accepts int) rather than a string.
Example fix
// before
port_from_config = "8080\n"
url = httpx.URL("http://example.com", port=port_from_config) # Invalid port
// after
port = int(port_from_config.strip())
url = httpx.URL("http://example.com", port=port) Defensive patterns
Strategy: validation
Validate before calling
def safe_port(port) -> int:
if isinstance(port, str):
port = port.strip()
p = int(port) # ValueError surfaces here with a clean traceback
if not (0 <= p <= 65535):
raise ValueError(f"Port out of range: {p}")
return p
url = httpx.URL("http://example.com", port=safe_port(port_cfg)) Type guard
def is_valid_port(value) -> bool:
try:
return 0 <= int(str(value).strip()) <= 65535
except (TypeError, ValueError):
return False Try / catch
from httpx import InvalidURL
try:
url = httpx.URL("http://h", port=raw_port)
except InvalidURL as e:
if "Invalid port" in str(e):
raise ValueError(f"Bad port from config: {raw_port!r}") from e
raise Prevention
- Coerce port to int at config-load time, not at URL build time.
- Strip whitespace/newlines from env-var-derived port strings.
- Validate the 0-65535 range explicitly.
When it happens
Trigger: httpx.URL('http://host:abc/'), httpx.URL(host='h', port='8080.0'), or passing port as a stringified float.
Common situations: Reading the port from a config file as a string with a trailing newline or unit ('8080\n', '8080/tcp'); passing a float by mistake; user input 'https'.
Related errors
- URL too long
- Invalid URL component '{key}'
- Proxy protocol must be either 'http', 'https', 'socks5', or
- Invalid non-printable ASCII character in URL, {char!r} at po
- URL component '{key}' too long
AI-assisted analysis of encode/httpx@b5addb64f0 (2026-08-04).
Data as JSON: /data/errors/5013c9d9d28a644f.json.
Report an issue: GitHub.