Repository navigation
[BUG] httpx.Client() crashes when NO_PROXY contains bracketed IPv6 [::1] — Invalid port ':1]' #1541
Description
Bug Description
Constructing a default httpx.Client() (with trust_env=True) crashes at startup when the process environment contains a bracketed IPv6 loopback in NO_PROXY / no_proxy, e.g. localhost,127.0.0.1,::1,[::1]. The [::1] entry is treated as a URL, its :1] suffix is parsed as a port, and client construction raises instead of starting.
This bit us in the field: an MCP host process inherited that NO_PROXY value, so every httpx.Client() created by Basic Memory's dependency tree blew up with Invalid port: ':1]' (same signature as the FastEmbed Invalid port failure). Removing just the [::1] entry (keeping bare ::1) fixes it.
Steps To Reproduce
- Install version
0.23.2(bundleshttpx 0.28.1). - Set the environment (Windows 11, but the parsing bug is platform-independent):
NO_PROXY=localhost,127.0.0.1,::1,[::1]no_proxy=localhost,127.0.0.1,::1,[::1]
- Run:
import httpx c = httpx.Client()
- See error (reproduced with the venv interpreter shipped by
uvx --prerelease=allow basic-memory mcp).
Control case: scrubbing the env in-process to NO_PROXY=localhost,127.0.0.1,::1 (bare IPv6, no brackets) plus HTTP_PROXY/HTTPS_PROXY/ALL_PROXY=http://127.0.0.1:7078 constructs the client fine.
Expected Behavior
A bracketed IPv6 loopback in NO_PROXY should be accepted (it is the standard URL-form of ::1, and tools like curl accept it), or at worst that single entry should be skipped with a warning. Client construction must not raise for a bypass-list entry.
Actual Behavior
httpx.Client() raises during __init__:
ValueError: invalid literal for int() with base 10: ':1]'
...
File "httpx/_client.py", line 698, in __init__
URLPattern(key): None
File "httpx/_utils.py", line 172, in __init__
url = URL(pattern)
File "httpx/_urls.py", line 117, in __init__
self._uri_reference = urlparse(url, **kwargs)
File "httpx/_urlparse.py", line 321, in urlparse
parsed_port: int | None = normalize_port(port, scheme)
File "httpx/_urlparse.py", line 411, in normalize_port
raise InvalidURL(f"Invalid port: {port!r}")
httpx.InvalidURL: Invalid port: ':1]'
So any code path that builds a default client while that NO_PROXY value is present fails before doing any work.
Environment
- OS: Windows 11 (10.0.26200)
- Python version: 3.12.12 (venv created by uv)
- Basic Memory version: 0.23.2
- Installation method:
uvx --prerelease=allow basic-memory mcp(uvx 0.11.3) - Bundled httpx version: 0.28.1
Additional Context
- Triggering value (process env):
NO_PROXY/no_proxy=localhost,127.0.0.1,::1,[::1]. The[::1]entry alone (NO_PROXY=[::1]) is sufficient to reproduce. - Safe value verified:
localhost,127.0.0.1,::1(bare::1, no brackets) works, including with the full proxy set (HTTP_PROXY/HTTPS_PROXY/ALL_PROXY=http://127.0.0.1:port). - Searched existing issues for NO_PROXY / brackets / httpx / invalid port and found no duplicate.
- The crash happens inside
httpx(_utils.URLPattern<-_clientproxy-map build fromget_environment_proxies), surfaced through Basic Memory because it is the shipped dependency. If httpx is vendored/pinned here, the fix belongs here; otherwise forwarding the minimal repro upstream to httpx would resolve it.
Possible Solution
Options, cheapest first:
- Sanitize
NO_PROXY/no_proxyentries before they reachURLPattern: strip one pair of surrounding brackets from bracketed IPv6 literals ([::1]->::1), consistent with URL host parsing. - Alternatively, catch
InvalidURLper bypass entry and skip/warn instead of failing client construction. - Alternatively, construct internal clients with
trust_env=False(or explicitproxy=/no_proxy=) on paths that must never be affected by ambient proxy env. - If the vendored httpx is old, pick up the upstream fix if one exists for bracketed
NO_PROXYhosts.
Workaround for affected users: drop [::1] from NO_PROXY/no_proxy, keeping localhost,127.0.0.1,::1.
Activity
phernandez commentedon Sep 14, 2026
Investigated against fetched
origin/mainatdff6656d94e5bd10a06617d70c2ec92403ca8f4ain the existing reusable worktree, on fresh branchcodex/fix-1541-ipv6-proxy.Disposition: blocked pending upstream fix / explicit product proxy-policy decision; no Core patch proposed.
Reproduction with the repository environment
uv run --locked pythonresolves httpx 0.28.1 (Python 3.14 in this worktree). No network request is needed. Child processes received explicit test environments; the parent environment and user configuration were not modified._build_asgi_client[::1]InvalidURL: Invalid port: ':1]'localhost,127.0.0.1,::1,[::1]localhost,127.0.0.1,::1These cases included HTTP_PROXY/HTTPS_PROXY/ALL_PROXY pointing at
http://127.0.0.1:7078. Separate uppercase-only and lowercase-only bypass-variable cases also reproduce through both Core's_default_http_client(30)and Hugging Face'sdefault_client_factory()(resolved huggingface-hub 1.23.0).Minimal command for upstream reproduction, scoped to a child process:
Root cause:
httpx._utils.get_environment_proxies()classifies[::1]as a hostname and emitsall://*[::1];URLPatternthen fails parsing its port. Bare::1instead takes the IPv6 branch.Upstream status
httpx>=0.28.0and locks 0.28.1; this is an ordinary dependency, not vendored code. There is no newer stable release to adopt.Complete Core httpx construction inventory and callers
There are five direct construction sites in
src/basic_memory:mcp/async_client.py:66,_build_asgi_client: explicit ASGITransport. httpx usesallow_env_proxies = trust_env and transport is None, so this client already skips environment proxy parsing and is unaffected. Addingtrust_env=Falsehere would not fix the reported failure.mcp/async_client.py:302,_cloud_client: network client for cloud project/proxy requests, reached through routing helpers and MCP/CLI typed clients.mcp/async_client.py:322,get_cloud_control_plane_client: network client for control-plane endpoints, also used by WebDAV push/pull transfer factories.cli/auth.py:52,CLIAuth._get_http_client: authentication network client, unless a caller injects its own factory.cli/commands/cloud/api_client.py:78,_default_http_client: generic cloud API requests, unless a caller injects its own factory.The typed MCP clients consume existing clients; they do not construct additional httpx transports. Upload uses
get_clientor its supplied factory; WebDAV transfer uses the control-plane factory or its supplied factory. The global Core client-factory hook delegates ownership to the embedding host.Dependency-owned paths also matter: Core's FastEmbed embedding and rerank providers construct
TextEmbedding/TextCrossEncoder; FastEmbed model management calls Hugging Facesnapshot_download, whose HTTP factory constructs its own default httpx.Client. That factory independently reproduces the crash. The OpenAI embedding provider constructsAsyncOpenAIwithout an injected HTTP client; LiteLLM delegates toaembedding, likewise outside the Core routing factory. Searching the integration/plugin/script sources found no additional direct Python httpx client construction sites.Why no narrow Core fix
Disabling environment handling on network clients would change user proxy routing and environment certificate behavior. Normalizing or dropping process-global proxy variables would affect the host and all concurrent dependency clients. A wrapper around only the four affected Core-owned network clients would leave the demonstrated Hugging Face/FastEmbed failure intact. Reimplementing environment proxy discovery/mount matching or replacing dependency-global HTTP factories would require a wider, explicit product contract and maintenance commitment.
The preferred resolution is upstream parsing support followed by a released dependency update. If Core must ship a workaround first, maintainers need to decide the supported proxy policy and dependency-client coverage explicitly. The existing user-controlled workaround is to replace the bracketed loopback entry with bare
::1, retaining other bypass entries and proxy settings.No source or lockfile changes, no PR, and no merge. Focused executable reproductions were run; fast-check/doctor were not run because no Core change was made. Worktree remains clean; dirty main checkout and existing topology were preserved.
phernandez commentedon Sep 14, 2026
Milestone status (2026-09-14): reproduced and understood; blocked on upstream or an explicit proxy-policy decision.
Fresh upstream read-back is unchanged: HTTPX's latest release is still 0.28.1, and encode/httpx#3741 remains open and addresses IPv6 CIDR rather than a released bracketed-literal fix. Core's local ASGI client is unaffected, but Core-owned network clients and dependency-owned Hugging Face/FastEmbed clients can still fail from
NO_PROXY=[::1].There is no narrow Core-only patch that fixes every affected client without changing ambient proxy semantics. Current workaround remains: use bare
::1instead of[::1]. Next step is either adopt an upstream release that fixes bracketed literals or make an explicit product decision about sanitizing process proxy configuration and dependency-client coverage. No PR is pending.FiretrUCK666 commentedon Sep 14, 2026
Follow-up: the bracketed entry is injected by the host harness at every launch, so the "drop
[::1]" workaround does not hold in such hosts.Traced the source of the reported
NO_PROXYvalue on my machine. It is not persisted anywhere — it is written into the environment at runtime, by the host process that launches the app, so editing the variable by hand does not survive a restart:@deepseek-ai/dsh-http-proxy0.1.5-rc.1). ItsLOOPBACK_NO_PROXYconstant is["localhost","127.0.0.1","::1","[::1]"](lib/index.js:19-24);withLoopback()merges it into the proxy policy andapplyPolicyEnv()writes the result back intoprocess.env.no_proxy/NO_PROXY(lines 334-347, 364-375).localhost,127.0.0.1,::1,[::1], and the bracket makes httpx 0.28.1 raiseInvalidURL: Invalid port: ':1]'before any work happens.localhost,127.0.0.1,::1); the bracketed form exists only in the process environment, and clearing it manually is undone on the next launch. That is why the documented workaround cannot be applied by affected users in this setup.The bracket was added for undici, whose older releases parsed a bare
::1as host:port1. That is no longer true for current undici (8.10.2 handles bare IPv6 explicitly inEnvHttpProxyAgent,lib/dispatcher/env-http-proxy-agent.js:120-139), so the injecting side can drop it — I am filing this with the host project and will note the outcome here. If it helps, this is a concrete "bracketed literal" case file for the upstream httpx parsing fix you are waiting on; nothing is required from Basic Memory in the meantime, and the local workaround (cleanNO_PROXYfor the MCP child process) is holding.phernandez commentedon Sep 14, 2026
Closing as external/not planned and removing from the v0.24.0 milestone.
The follow-up identified the root cause in the launching host: DeepSeek Harness's
@deepseek-ai/dsh-http-proxyinjects the redundant bracketed IPv6 entry[::1]into every child process alongside bare::1. HTTPX 0.28.1 then fails while parsing that inheritedNO_PROXYvalue. The reporter is taking the correction to the host, where the entry can be removed for every launched child.Basic Memory will not mutate process-global proxy variables or disable
trust_env:trust_env=Falsewould break legitimate proxy/CA configuration;The appropriate fixes are for the injecting host to stop adding
[::1], and optionally for HTTPX upstream to tolerate the bracketed literal. No Core code change or release blocker remains.