pypa/pip · error · NetworkConnectionError

Client Error: for url

Error message

{resp.status_code} Client Error: {reason} for url: {resp.url}

What it means

Raised as NetworkConnectionError by raise_for_status when an HTTP response carries a 4xx (Client Error) or 5xx (Server Error) status code. The message format is '{status_code} Client Error: {reason} for url: {url}' (and the Server Error variant for 5xx). The original requests Response is attached as .response so callers can inspect status, headers, and body.

Solutions

  1. Verify the package name and the configured index URL (pip config list; pip install -v).
  2. Provide credentials if the status is 401/403 (auth in URL, keyring, or --index-url with creds).
  3. Retry on transient 5xx errors; switch to an alternate mirror if persistent.
  4. Check pip.conf for a stale/mistyped index-url or extra-index-url.
Defensive patterns

Strategy: try-catch

Try / catch

from pip._internal.exceptions import NetworkConnectionError

try:
    resp = session.get(url)
    raise_for_status(resp)
except NetworkConnectionError as e:
    status = e.response.status_code if e.response is not None else None
    if status in (401, 403):
        # supply credentials and retry
        ...
    elif status and 500 <= status < 600:
        # transient server error, retry with backoff
        ...
    else:
        raise

Prevention

When it happens

Trigger: pip performs an HTTP GET (index simple page, JSON API, file download) and the server replies with 401/403/404/500/502/503 etc.; raise_for_status converts that into an exception.

Common situations: Wrong package name or index URL (404); private index requiring auth (401/403); index server outage or gateway error (500/502/503); rate limiting; expired credentials.

Related errors


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

Appendix: source

Thrown at src/pip/_internal/network/utils.py:71

        try:
            reason = resp.reason.decode("utf-8")
        except UnicodeDecodeError:
            reason = resp.reason.decode("iso-8859-1")
    else:
        reason = resp.reason

    if 400 <= resp.status_code < 500:
        http_error_msg = (
            f"{resp.status_code} Client Error: {reason} for url: {resp.url}"
        )

    elif 500 <= resp.status_code < 600:
        http_error_msg = (
            f"{resp.status_code} Server Error: {reason} for url: {resp.url}"
        )

    if http_error_msg:
        raise NetworkConnectionError(http_error_msg, response=resp)


def response_chunks(
    response: Response, chunk_size: int = DOWNLOAD_CHUNK_SIZE
) -> Generator[bytes, None, None]:
    """Given a requests Response, provide the data chunks."""
    try:
        # Special case for urllib3.
        for chunk in response.raw.stream(
            chunk_size,
            # We use decode_content=False here because we don't
            # want urllib3 to mess with the raw bytes we get
            # from the server. If we decompress inside of
            # urllib3 then we cannot verify the checksum
            # because the checksum will be of the compressed
            # file. This breakage will only occur if the
            # server adds a Content-Encoding header, which
            # depends on how the server was configured:

View on GitHub (pinned to f399c37189)