aio-libs/aiohttp · error · ClientHttpProxyError
{reason}
Error message
{reason} What it means
Raised by _create_proxy_connection() when the proxy's response to the CONNECT request is not HTTP 200. aiohttp builds the message from resp.reason (or the standard HTTPStatus phrase for the code if reason is missing) and raises ClientHttpProxyError, a ClientResponseError carrying status, headers, and history. Non-200 from CONNECT typically means 407 (auth required / failed) or 403/502 (proxy policy).
Source
Thrown at aiohttp/connector.py:1641
# once the response is received and processed allowing
# START_TLS to work on the connection below.
protocol.set_response_params(
read_until_eof=True,
timeout_ceil_threshold=self._timeout_ceil_threshold,
)
resp = await proxy_resp.start(conn)
except BaseException:
proxy_resp.close()
conn.close()
raise
else:
conn._protocol = None
try:
if resp.status != 200:
message = resp.reason
if message is None:
message = HTTPStatus(resp.status).phrase
raise ClientHttpProxyError(
proxy_resp.request_info,
resp.history,
status=resp.status,
message=message,
headers=resp.headers,
)
except BaseException:
# It shouldn't be closed in `finally` because it's fed to
# `loop.start_tls()` and the docs say not to touch it after
# passing there.
transport.close()
raise
return await self._start_tls_connection(
# Access the old transport for the last time before it's
# closed and forgotten forever:
transport,
req=req,View on GitHub (pinned to c0ef574e29)
Solutions
- For 407: supply proxy auth via `aiohttp.BasicAuth` in the `proxy_headers=` argument or in the proxy URL (`http://user:pass@proxy:8080`).
- Confirm the target host/port is allowed by the proxy's ACL.
- Read `exc.status`, `exc.headers`, and the body for the proxy's reason text.
- Switch to a proxy that permits CONNECT to the destination, or use a direct connection.
Example fix
# before
async with session.get('https://target', proxy='http://proxy:8080') as r: ...
# 407 Proxy Authentication Required
# after
import aiohttp
auth = aiohttp.BasicAuth('user', 'pass')
async with session.get(
'https://target',
proxy='http://proxy:8080',
proxy_headers={'Proxy-Authorization': auth.encode()},
) as r: ... Defensive patterns
Strategy: try-catch
Validate before calling
import aiohttp
def proxy_auth_header(user: str, pw: str) -> dict:
return {'Proxy-Authorization': aiohttp.BasicAuth(user, pw).encode()} Try / catch
from aiohttp import ClientHttpProxyError
try:
await session.get(url, proxy=proxy_url, proxy_headers=proxy_auth_header(u, p))
except ClientHttpProxyError as e:
if e.status == 407:
# refresh credentials and retry once
...
raise Prevention
- Always supply Proxy-Authorization when the proxy requires auth; test against a known endpoint.
- Read exc.status/headers/body to distinguish auth failures from policy failures.
- Confirm the proxy allows CONNECT to the target host:port before relying on it.
When it happens
Trigger: Proxy returned 407 Proxy Authentication Required (bad/missing Proxy-Authorization), 403 Forbidden, 502/503, or any non-200 status to the CONNECT establishing the tunnel.
Common situations: Proxy credentials missing or wrong. Proxy allowlist excludes the target host. Proxy rate-limiting. Squid/forward-proxy returning 407 on first request because auth header wasn't supplied. HTTP proxy that refuses CONNECT to certain ports.
Related errors
- Cannot initialize a TLS-in-TLS connection to host {req.url.h
- Cannot combine AUTHORIZATION header with credentials encoded
- Invalid upgrade header
- Invalid connection header
- None is not allowed as login value
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/c1e3ebfdb3842305.json.
Report an issue: GitHub.