pypa/pip · error · ValueError

Trusted host URL must include a host part

Error message

Trusted host URL must include a host part: {host!r}

What it means

Raised as a ValueError by PipSession.add_trusted_host when parse_netloc(host) returns None for the host component, i.e. the --trusted-host value (or a trusted-host entry from pip.conf) has no parseable hostname. A trusted host must carry at least a host part.

Solutions

  1. Provide a hostname: --trusted-host pypi.org or --trusted-host my.server:8080.
  2. If using a full URL, ensure it contains a host, e.g. http://my.server.
  3. Audit pip.conf / requirements for malformed trusted-host entries.

Example fix

# before
pip install --trusted-host ":8080" pkg

# after
pip install --trusted-host my.server:8080 pkg
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlsplit

def trusted_host_has_host(host: str) -> bool:
    parsed = host if "://" in host else f"//{host}"
    return urlsplit(parsed, allow_fragments=False).hostname is not None

# assert trusted_host_has_host("pypi.org")
# assert not trusted_host_has_host(":8080")

Prevention

When it happens

Trigger: Passing pip --trusted-host with a port-only or path-only or empty string (e.g. ':8080', '/path', 'http://'), or a malformed trusted-host line in config, so parse_netloc yields no host.

Common situations: Typo in --trusted-host; copy-pasting a URL fragment instead of a host; config automation that emits an empty trusted-host value; trailing slashes or scheme-only strings.

Related errors


AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08). Data as JSON: /api/errors/e2c779e5d3bc0ed0. Report an issue: GitHub.

Appendix: source

Thrown at src/pip/_internal/network/session.py:426

    def add_trusted_host(
        self, host: str, source: str | None = None, suppress_logging: bool = False
    ) -> None:
        """
        :param host: It is okay to provide a host that has previously been
            added.
        :param source: An optional source string, for logging where the host
            string came from.
        """
        if not suppress_logging:
            msg = f"adding trusted host: {host!r}"
            if source is not None:
                msg += f" (from {source})"
            logger.info(msg)

        parsed_host, parsed_port = parse_netloc(host)
        if parsed_host is None:
            raise ValueError(f"Trusted host URL must include a host part: {host!r}")
        if (parsed_host, parsed_port) not in self.pip_trusted_origins:
            self.pip_trusted_origins.append((parsed_host, parsed_port))

        self.mount(
            build_url_from_netloc(host, scheme="http") + "/", self._trusted_host_adapter
        )
        self.mount(build_url_from_netloc(host) + "/", self._trusted_host_adapter)
        if not parsed_port:
            self.mount(
                build_url_from_netloc(host, scheme="http") + ":",
                self._trusted_host_adapter,
            )
            # Mount wildcard ports for the same host.
            self.mount(build_url_from_netloc(host) + ":", self._trusted_host_adapter)

    def iter_secure_origins(self) -> Generator[SecureOrigin, None, None]:
        yield from SECURE_ORIGINS
        for host, port in self.pip_trusted_origins:

View on GitHub (pinned to f399c37189)