You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Not support "no_proxy" including value of ipv6 prefix style #3221
no_proxy=fe11::/16 python -c 'import httpx; c = httpx.Client()'
it will raise:
Traceback (most recent call last):
File "/usr/local/lib/python3.9/dist-packages/httpx/_urlparse.py", line 346, in normalize_port
port_as_int = int(port)
ValueError: invalid literal for int() with base 10: ':'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/usr/local/lib/python3.9/dist-packages/httpx/_client.py", line 695, in __init__
self._mounts: dict[URLPattern, BaseTransport | None] = {
File "/usr/local/lib/python3.9/dist-packages/httpx/_client.py", line 696, in <dictcomp>
URLPattern(key): None
File "/usr/local/lib/python3.9/dist-packages/httpx/_utils.py", line 370, in __init__
url = URL(pattern)
File "/usr/local/lib/python3.9/dist-packages/httpx/_urls.py", line 115, in __init__
self._uri_reference = urlparse(url, **kwargs)
File "/usr/local/lib/python3.9/dist-packages/httpx/_urlparse.py", line 248, in urlparse
parsed_port: int | None = normalize_port(port, scheme)
File "/usr/local/lib/python3.9/dist-packages/httpx/_urlparse.py", line 348, in normalize_port
raise InvalidURL(f"Invalid port: {port!r}")
httpx.InvalidURL: Invalid port: ':'
changed the title [-]Not support "no_proxy" including pattern of ipv6 prefix style[/-][+]Not support "no_proxy" including value of ipv6 prefix style[/+]on Jun 12, 2024
For no_proxy=127.0.0.0/8, get_environment_proxies() will get all://127.0.0.0/8.
So, when creating a Client, in which URL class will be called, /8 will be treated as a URL path finally.
I also encountered this issue when using proxy in company's network. We use proxy to visit outside networks and automatically set no_proxy with both ipv4/ipv6 masking to prevent proxying specific ranges of IPs, such as 127.0.0.0/8,169.254.0.0/16,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16,10.0.0.0/8,::1,fe80::/10,fd00::/8.
For no_proxy, I think we should address the issue in phases, so create a branch rather than do all in the main one.
Let’s fix the crash problem first, rather than implementing the full semantic functionality (like subnet mask support) in httpx. Since no_proxy is commonly used in VPN and Zero Trust setups, It could be to adapt the no_proxy parser to handle these cases and prevent crashes at the first step.
Of course, just preventing crashes isn’t a perfect solution, it will break the intended semantics of no_proxy and could lead to unexpected behavior.
I also suggest considering a refactor of the proxy handling in httpx, allowing dispatching at both the domain and the ip:port level. However, this might be too extreme and could cause a breaking change to the API. I’d love to hear ideas from you all.
As for some potential concerns, here are my quick responses:
Is ensuring stability and preventing application crashes a valid intermediate step?
I think this could change behavior between versions, which I’m not comfortable with—even with a warning mechanism in place.
Is limiting or configurable advanced features acceptable?
That would compromise the consistency of the API and break the intended semantics of no_proxy inside.
Could this introduce too much complexity?
I’m not sure, so I wander tom on this.
How do other HTTP libraries handle subnet masks? Anything to learn from them?
Some languages do often require extra configuration(java, nodejs), but Python’s tradition is to make things work out-of-the-box.
(I doubt other languages would adopt the idea of ignoring environment variables by default.)
And httpx==0.28.1 is installed by default with vllm or some other packages.
When I want to do httpx.get(something), there must be an error:
Traceback (most recent call last):
File "/home/jinyu121/.pyenv/versions/3.11.2/lib/python3.11/site-packages/httpx/_urlparse.py", line 409, in normalize_port
port_as_int = int(port)
^^^^^^^^^
ValueError: invalid literal for int() with base 10: ':'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
(... my code is omitted there ...)
File "/home/jinyu121/.pyenv/versions/3.11.2/lib/python3.11/site-packages/httpx/_api.py", line 195, in get
return request(
^^^^^^^^
File "/home/jinyu121/.pyenv/versions/3.11.2/lib/python3.11/site-packages/httpx/_api.py", line 102, in request
with Client(
^^^^^^^
File "/home/jinyu121/.pyenv/versions/3.11.2/lib/python3.11/site-packages/httpx/_client.py", line 697, in __init__
self._mounts: dict[URLPattern, BaseTransport | None] = {
^
File "/home/jinyu121/.pyenv/versions/3.11.2/lib/python3.11/site-packages/httpx/_client.py", line 698, in <dictcomp>
URLPattern(key): None
^^^^^^^^^^^^^^^
File "/home/jinyu121/.pyenv/versions/3.11.2/lib/python3.11/site-packages/httpx/_utils.py", line 172, in __init__
url = URL(pattern)
^^^^^^^^^^^^
File "/home/jinyu121/.pyenv/versions/3.11.2/lib/python3.11/site-packages/httpx/_urls.py", line 117, in __init__
self._uri_reference = urlparse(url, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jinyu121/.pyenv/versions/3.11.2/lib/python3.11/site-packages/httpx/_urlparse.py", line 321, in urlparse
parsed_port: int | None = normalize_port(port, scheme)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jinyu121/.pyenv/versions/3.11.2/lib/python3.11/site-packages/httpx/_urlparse.py", line 411, in normalize_port
raise InvalidURL(f"Invalid port: {port!r}")
httpx.InvalidURL: Invalid port: ':'
I tried to upgrade httpx to the newest version, but the problem isn't solved.
The only way to solve this is to downgrade httpx to 0.23.3 via pip install httpx=0.23.3. I have to do this every time a dev environment is established. It's so annoying.
No we don't currently support subnet masks here, yes we should at least raise an error if they're in play.
Aside... what's an example of a real-world scenario where it's useful to have a subnet masking used to toggle proxy routing on/off?
I think printing a WARNING is better than raising an error httpx.InvalidURL: Invalid port: ':]' that confusing. I think this is a helpful message style: WARNING: no_proxy entry ::1/128 will be ignored: httpx.InvalidURL: Invalid port: ':'.
Adding a data point for a bracketed IPv6 loopback entry, which I believe is
a distinct and more widespread variant than the IPv6-CIDR case in the original report.
NO_PROXY=127.0.0.1,localhost,::1,[::1] is the exact value Clash Verge / mihomo
exports by default on Windows (and Linux). Any Python application using a bare httpx.Client() / httpx.get() then fails at client construction — before the first
request is even attempted.
Mechanism
In httpx/_utils.py::get_environment_proxies, a bracketed entry misses every
recognised branch:
elifis_ipv4_hostname(hostname): # [::1] -> Falseelifis_ipv6_hostname(hostname): # [::1] -> False (brackets are not stripped)elifhostname.lower() =="localhost": # Falseelse:
mounts[f"all://*{hostname}"] =None# -> "all://*[::1]"
is_ipv6_hostname("[::1]") returns False, so the entry falls into the wildcard
branch and produces the unparseable pattern all://*[::1]. URLPattern then
URL-parses it and treats :1] as the port.
Measured difference between bare and bracketed
NO_PROXY value
resulting mount key
httpx.Client()
::1 (bare)
all://[::1]
✅ constructs
[::1] (bracketed)
all://*[::1]
❌ InvalidURL: Invalid port: ':1]'
[::1]:8080
all://*[::1]:8080
❌ Invalid port: ':1]:8080'
::1/128
all://[::1/128]
❌ Invalid port: ':1'
http://example.com
http://example.com
✅ constructs
So the bare form already works (fixed by #2659); the bracketed form — the one
proxy tools actually emit — still crashes. It is dead weight in other consumers too: urllib.request.proxy_bypass_environment("::1", {"no": "[::1]"}) returns False
(verified locally against CPython 3.12), so nothing matches the bracketed form. It is
only meaningful as URL-authority syntax, which is why proxy UIs emit it, but it is not
valid NO_PROXY syntax.
Suggested fix
Treat bracketed IPv6 as an IPv6 hostname rather than falling through to the wildcard
branch, e.g. strip a leading [ / trailing ] (and any :port suffix) before the is_ipv6_hostname check. That keeps the existing bypass semantics — ::1 already maps
to all://[::1] and bypasses every port — while no longer crashing on the form proxy
software actually writes.
Downstream impact
This is being patched independently in a number of projects, which suggests the blast
radius is wide: e.g. NousResearch/hermes-agent#131802 (merged), MoonshotAI/kimi-code#2186, safedep/pmg#340, and mistralai/mistral-vibe#759. A fix upstream would let all of them
drop their local workarounds.
Thanks for looking at this — happy to test a patch against the Clash Verge environment if
that would help.
Activity
[-]Not support "no_proxy" including pattern of ipv6 prefix style[/-][+]Not support "no_proxy" including value of ipv6 prefix style[/+]matstrand commentedon Aug 28, 2024
Another test case to throw in that is affecting us:
Godsing commentedon Sep 10, 2024
For
no_proxy=127.0.0.0/8,get_environment_proxies()will getall://127.0.0.0/8.So, when creating a
Client, in whichURLclass will be called,/8will be treated as a URLpathfinally.python -c 'import httpx; url = httpx.URL("all://127.0.0.0/8"); print(url.path)' /8httpx version: 0.27.0
lovelydinosaur commentedon Sep 10, 2024
Duplicate of #1536
No we don't currently support subnet masks here, yes we should at least raise an error if they're in play.
Aside... what's an example of a real-world scenario where it's useful to have a subnet masking used to toggle proxy routing on/off?
yanyongyu commentedon Sep 29, 2024
I also encountered this issue when using proxy in company's network. We use proxy to visit outside networks and automatically set
no_proxywith both ipv4/ipv6 masking to prevent proxying specific ranges of IPs, such as127.0.0.0/8,169.254.0.0/16,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16,10.0.0.0/8,::1,fe80::/10,fd00::/8.GreyElaina commentedon Dec 7, 2024
Here’s my take:
For no_proxy, I think we should address the issue in phases, so create a branch rather than do all in the main one.
Let’s fix the crash problem first, rather than implementing the full semantic functionality (like subnet mask support) in httpx. Since no_proxy is commonly used in VPN and Zero Trust setups, It could be to adapt the no_proxy parser to handle these cases and prevent crashes at the first step.
Of course, just preventing crashes isn’t a perfect solution, it will break the intended semantics of no_proxy and could lead to unexpected behavior.
I also suggest considering a refactor of the proxy handling in httpx, allowing dispatching at both the domain and the ip:port level. However, this might be too extreme and could cause a breaking change to the API. I’d love to hear ideas from you all.
As for some potential concerns, here are my quick responses:
I think this could change behavior between versions, which I’m not comfortable with—even with a warning mechanism in place.
That would compromise the consistency of the API and break the intended semantics of no_proxy inside.
I’m not sure, so I wander tom on this.
Some languages do often require extra configuration(java, nodejs), but Python’s tradition is to make things work out-of-the-box.
(I doubt other languages would adopt the idea of ignoring environment variables by default.)
jinyu121 commentedon May 23, 2025
I'm also facing this issue.
In my environment, the following
no_proxyenvironment variable is set on every machine. It looks so normal and standard:And
httpx==0.28.1is installed by default with vllm or some other packages.When I want to do
httpx.get(something), there must be an error:I tried to upgrade
httpxto the newest version, but the problem isn't solved.The only way to solve this is to downgrade httpx to 0.23.3 via
pip install httpx=0.23.3. I have to do this every time a dev environment is established. It's so annoying.Could anybody solve this? Thanks a lot.
Fix no_proxy to support IPV6 CIDR format
Fix no_proxy to support IPV6 CIDR format
p3ck commentedon Jan 6, 2026
@jinyu121 Can you try my PR from above and tell me if everything works as expected?
Thanks
Indekkusu545 commentedon Feb 4, 2026
I think printing a WARNING is better than raising an error
httpx.InvalidURL: Invalid port: ':]'that confusing. I think this is a helpful message style:WARNING: no_proxy entry ::1/128 will be ignored: httpx.InvalidURL: Invalid port: ':'.release: v0.7.3
DDDMUC commentedon Oct 16, 2026
Adding a data point for a bracketed IPv6 loopback entry, which I believe is
a distinct and more widespread variant than the IPv6-CIDR case in the original report.
Reproduction
httpx version:
0.28.1Where it comes from
NO_PROXY=127.0.0.1,localhost,::1,[::1]is the exact value Clash Verge / mihomoexports by default on Windows (and Linux). Any Python application using a bare
httpx.Client()/httpx.get()then fails at client construction — before the firstrequest is even attempted.
Mechanism
In
httpx/_utils.py::get_environment_proxies, a bracketed entry misses everyrecognised branch:
is_ipv6_hostname("[::1]")returnsFalse, so the entry falls into the wildcardbranch and produces the unparseable pattern
all://*[::1].URLPatternthenURL-parses it and treats
:1]as the port.Measured difference between bare and bracketed
NO_PROXYvaluehttpx.Client()::1(bare)all://[::1][::1](bracketed)all://*[::1]InvalidURL: Invalid port: ':1]'[::1]:8080all://*[::1]:8080Invalid port: ':1]:8080'::1/128all://[::1/128]Invalid port: ':1'http://example.comhttp://example.comSo the bare form already works (fixed by #2659); the bracketed form — the one
proxy tools actually emit — still crashes. It is dead weight in other consumers too:
urllib.request.proxy_bypass_environment("::1", {"no": "[::1]"})returnsFalse(verified locally against CPython 3.12), so nothing matches the bracketed form. It is
only meaningful as URL-authority syntax, which is why proxy UIs emit it, but it is not
valid
NO_PROXYsyntax.Suggested fix
Treat bracketed IPv6 as an IPv6 hostname rather than falling through to the wildcard
branch, e.g. strip a leading
[/ trailing](and any:portsuffix) before theis_ipv6_hostnamecheck. That keeps the existing bypass semantics —::1already mapsto
all://[::1]and bypasses every port — while no longer crashing on the form proxysoftware actually writes.
Downstream impact
This is being patched independently in a number of projects, which suggests the blast
radius is wide: e.g.
NousResearch/hermes-agent#131802(merged),MoonshotAI/kimi-code#2186,safedep/pmg#340, andmistralai/mistral-vibe#759. A fix upstream would let all of themdrop their local workarounds.
Thanks for looking at this — happy to test a patch against the Clash Verge environment if
that would help.