Geonode logo
Library and script errors

How to Fix requests.exceptions.ProxyError in Python

A map from each Caused by message to its cause, plus three Python fixes, most common first.

Updated

TL;DR

requests.exceptions.ProxyError means Python requests could not get through its proxy, which was unreachable, refused the CONNECT tunnel or spoke another protocol, so no website saw the request. The fastest fix is to correct whatever the error after Caused by names, usually the proxy host, port or URL scheme.

What ProxyError looks like in each client

Captured on macOS, so Linux and Windows print other errno numbers; the conda row comes from conda's source.

WhereWhat you see
requests 2.33 with urllib3 2.6 (proxy host name does not resolve)requests.exceptions.ProxyError: HTTPSConnectionPool(host='example.com', port=443): Max retries exceeded with url: / (Caused by ProxyError('Unable to connect to proxy', NameResolutionError("HTTPSConnection(host='proxy.invalid', port=9000): Failed to resolve 'proxy.invalid' ([Errno 8] nodename nor servname provided, or not known)")))
requests 2.31 with urllib3 1.26 (nothing listens on the proxy port)(Caused by ProxyError('Cannot connect to proxy.', NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x101aaeab0>: Failed to establish a new connection: [Errno 61] Connection refused')))
requests with urllib3 2.x (proxy answers CONNECT with 502)(Caused by ProxyError('Unable to connect to proxy', OSError('Tunnel connection failed: 502 Bad Gateway')))
requests with urllib3 2.x, raised as requests.exceptions.ConnectionError (proxy closes the connection without answering CONNECT)('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
pip 24.0 (bundled urllib3 1.26, as in pip up to 26.0)WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ProxyError('Cannot connect to proxy.', NewConnectionError('<pip._vendor.urllib3.connection.HTTPSConnection object at 0x102392750>: Failed to establish a new connection: [Errno 61] Connection refused'))': /simple/requests/
conda (raised when requests reports a ProxyError)Conda cannot proceed due to an error in your proxy configuration.

Why this happens

requests talks to the proxy first, and that first step failed.

urllib3, the library under requests, wraps most failures on the way to the proxy, such as a refused connection or a 407 reply to CONNECT, in its own ProxyError. The second item inside ProxyError(...) names the real cause.

Diagnose your ProxyError first

Each check reads one part of the Caused by text and names the cause or the page that covers it.

  • Look for your proxy's host in the Caused by part; another host there means requests took its proxy from environment or system settings.

  • If the inner error is NameResolutionError or NewConnectionError, requests never reached a proxy at that host and port; ConnectTimeoutError means a proxy timeout.

  • If it says Tunnel connection failed: 407, the proxy wants a valid login; other codes are refusals explained on the ERR_TUNNEL_CONNECTION_FAILED page.

  • If it says Your proxy appears to only use HTTP, your https:// proxy URL reached a plain HTTP port; the EPROTO page covers this.

  • If the inner error is RemoteDisconnected or ConnectionResetError, the proxy hung up without answering CONNECT; one cause is an http:// URL on a SOCKS5 port.

Solutions ranked by effectiveness

Start with the proxy URL itself, then with proxies that requests picks up on its own.

  1. Most common fix

    Re-enter the proxy host and port

    Applies when requests never got to your own proxy, for example after a typo in host or port. Copy both again from your dashboard into one http:// URL for both keys.

    # pip install requests
    import requests
    
    proxy = "http://USERNAME:PASSWORD@proxy.geonode.io:9000"
    proxies = {"http": proxy, "https": proxy}
    try:
        r = requests.get("https://example.com/", proxies=proxies, timeout=30)
        print(r.status_code)
    except requests.exceptions.ConnectionError as err:
        reason = getattr(err.args[0], "reason", err.args[0])
        inner = getattr(reason, "original_error", reason)
        print(type(err).__name__, "->", type(inner).__name__, inner)
  2. Check next

    Keep only the proxy your code sets

    Applies to any variant above when the proxy in the error is not yours. requests adds HTTPS_PROXY, ALL_PROXY or OS proxy settings ahead of session.proxies; trust_env = False turns that off.

    # pip install requests
    import requests
    
    url = "https://example.com/"
    proxy = "http://USERNAME:PASSWORD@proxy.geonode.io:9000"
    print("Ignoring:", requests.utils.get_environ_proxies(url))
    session = requests.Session()
    session.trust_env = False
    session.proxies = {"http": proxy, "https": proxy}
    print(session.get(url, timeout=30).status_code)
  3. For developers

    Match the URL scheme to the port

    Applies when the proxy hangs up on CONNECT. A SOCKS5 port does not speak HTTP, so a proxy URL's scheme must name the protocol of its port.

    1. Use http:// with Geonode's HTTP ports, 9000-9010 rotating or 10000-10900 sticky.

    2. For SOCKS5 ports 11000-11010 or 12000-12010, run pip install "requests[socks]" first.

    3. Then write socks5h:// so the proxy, not your machine, resolves site names.

Stop the ProxyError from coming back

Habits that keep stale proxy variables out of your requests and every proxy failure in one handler.

  1. Clear both spellings of a proxy variable: Python prefers https_proxy over HTTPS_PROXY, and an empty https_proxy disables both.

  2. After trust_env = False, pass verify= and auth yourself; the session also stops reading REQUESTS_CA_BUNDLE and .netrc.

  3. Remember that NO_PROXY, in the environment or as proxies["no_proxy"], filters only environment proxies, never one set in code.

  4. Log the inner exception's class, not its text; urllib3 1.26 and 2.x word the same failure differently.

  5. Catch requests.exceptions.ConnectionError as well, so a proxy hang-up or a SOCKS failure reaches the same handler.

One login for HTTP and SOCKS5Your Geonode username and password work on both the HTTP and the SOCKS5 port ranges.
Try residential proxies

Related errors

Learn more

FAQ

It is the exception Python requests raises when the hop to its proxy fails, such as a dead proxy address or a refused tunnel. A website's refusal, like a 403 inside a working tunnel, comes back as a normal response.

urllib3 2.x prints it when it could not reach the proxy or the proxy would not open a tunnel. An http:// target needs no tunnel, so there a proxy's 407 or 502 arrives as an ordinary response instead.

It is the wording of urllib3 1.26, which pip bundles up to version 26.0, for a failed proxy hop. Run pip config debug to see whether PIP_PROXY or a proxy line in pip.conf sets the proxy; pip also reads https_proxy.

The Exceptions list in the API reference skips it, though it includes its parent, ConnectionError. The class sits in requests.exceptions with the docstring "A proxy error occurred", and proxy setup is under Advanced Usage, Proxies.

With requests[socks] installed, use socks5h://127.0.0.1:9050, Tor's default SocksPort, for both keys. An http:// URL on that port fails with Tunnel connection failed: 501 Tor is not an HTTP Proxy.

Pass proxies={"http": url, "https": url}, where each key is the target site's scheme and url is the proxy address. A key such as "https://api.example.com" or "all" sets a proxy for one host or for every scheme.

Measure proxy latency in Python

Geonode's test script, built on requests, reports average and median latency through your proxies.