Repository navigation
Conversation
|
@ker2xu hi, and thank you for this PR! 🙏 This was a pleasure to pick up. The I reproduced the failure, and your diagnosis is right: HTTPX rejects a standalone bracketed IPv6 entry such as That said, I'd suggest reworking this before it's merged, mainly because of where the fix lives. The Summary
1. The crash isn't specific to AntigravityWhere
Who receives it from there:
What breaks, for default clients, which read the environment:
So with a proxy set in codeg's Settings, the same failure can hit any Python code that inherits this list and builds a default HTTPX or SDK client: a script that an agent runs through its shell tool, a Python MCP server under another agent that passes its environment down, or a script run in the built-in terminal. This PR covers processes under Antigravity's launch environment only (connection.rs L403-L410). For reference, the mechanism: 2. It's one trade-off, and it's globalYour point about Node holds up, and it isn't only Node. I sent a request to
Removing So the choice is between:
I'd favor dropping it. Today's value stops HTTPX clients from being created at all, even for unrelated destinations, while the routing change only affects IPv6-literal loopback URLs and can be documented. The loopback traffic behind #804 uses IPv4: for OpenCode builds that support the listen flags, codeg passes The PR already makes this trade, just inside one subtree: a Node or Bun process started under Antigravity, for example an 3. Suggested directionRemoving the bracketed default where it's built takes the codeg-generated
Your helper and its unit test already live in This fixes codeg's own default. Normalizing a value that is only inherited, in a launch without a proxy variable (§4), would be a separate follow-up. 4. Smaller notes on the current diffThese mostly go away with §3, but for completeness:
5. Tests and manual checksTests:
Manual, with a proxy set in Settings. Fully restart codeg first, so that fresh terminals and agent sessions pick up the new environment:
How I measured this (macOS 27, Python 3.13, httpx 0.28.1, requests 2.34.2, Node 24.18, Bun 1.3.14, curl 8.7.1)Client construction with today's list. uv installs the pinned packages with your normal network settings first, and the bypass list is only set inside Python: uv run -q --no-project --with 'httpx==0.28.1' --with 'openai==3.26.1' python - <<'EOF'
import os
os.environ["NO_PROXY"] = os.environ["no_proxy"] = "localhost,127.0.0.1,::1,[::1]"
import httpx, openai
for name, build in [("httpx.Client()", httpx.Client), ("openai.OpenAI()", lambda: openai.OpenAI(api_key="x"))]:
try:
build()
print(f"{name}: OK")
except Exception as e:
print(f"{name}: {type(e).__module__}.{type(e).__name__}: {e}")
EOF
# httpx.Client(): httpx.InvalidURL: Invalid port: ':1]'
# openai.OpenAI(): httpx2.InvalidURL: Invalid port: ':1]'The table in §2. Save this as #!/usr/bin/env bash
# Needs uv, node (24+), bun and curl on PATH, and a working IPv6 loopback.
cd "$(mktemp -d)" || exit 1
uv venv -q .venv && uv pip install -q --python .venv 'httpx==0.28.1' 'requests==2.34.2' || exit 1
cat > servers.py <<'EOF'
import socket, threading, time
def serve(sock, tag, status):
while True:
conn, _ = sock.accept()
conn.recv(4096)
with open("hits.log", "a") as log:
log.write(tag + "\n")
conn.sendall(f"HTTP/1.1 {status}\r\nContent-Length: 0\r\nConnection: close\r\n\r\n".encode())
conn.close()
proxy = socket.create_server(("127.0.0.1", 18999)) # stands in for a proxy on another machine
target = socket.create_server(("::1", 18998), family=socket.AF_INET6)
threading.Thread(target=serve, args=(proxy, "proxy", "502 Bad Gateway"), daemon=True).start()
threading.Thread(target=serve, args=(target, "direct", "200 OK"), daemon=True).start()
open("ready", "w").close()
time.sleep(600)
EOF
.venv/bin/python servers.py 2>servers.err & SERVERS=$!
trap 'kill $SERVERS 2>/dev/null' EXIT
for _ in 1 2 3 4 5 6 7 8 9 10; do [ -e ready ] && break; sleep 0.5; done
[ -e ready ] || { echo "The listeners did not start:"; cat servers.err; exit 1; }
URL='http://[::1]:18998/'
PX='http://127.0.0.1:18999'
PY="$PWD/.venv/bin/python"
JS_FETCH="fetch('$URL', { signal: AbortSignal.timeout(3000) }).then(r => r.text()).catch(e => console.error(String(e)))"
JS_HTTP="require('http').get('$URL', r => r.resume()).setTimeout(3000, function () { this.destroy() }).on('error', e => console.error(String(e)))"
probe() { # probe <label> <command...>, run with the NO_PROXY in $NP
local label=$1; shift
: > hits.log
env -i PATH="$PATH" HOME="$HOME" HTTP_PROXY=$PX http_proxy=$PX HTTPS_PROXY=$PX https_proxy=$PX \
NO_PROXY="$NP" no_proxy="$NP" "$@" >/dev/null 2>"$label.err"
sleep 0.2
local hits; hits=$(cat hits.log)
printf ' %-11s %s\n' "$label" "${hits:-no request sent: $(tail -n 1 "$label.err")}"
}
for NP in 'localhost,127.0.0.1,::1,[::1]' 'localhost,127.0.0.1,::1'; do
echo "NO_PROXY=$NP"
probe urllib "$PY" -c "import urllib.request as u; u.urlopen('$URL', timeout=3)"
probe requests "$PY" -c "import requests; requests.get('$URL', timeout=3)"
probe httpx "$PY" -c "import httpx; httpx.get('$URL', timeout=3)"
probe node-fetch env NODE_USE_ENV_PROXY=1 node -e "$JS_FETCH"
probe node-http env NODE_USE_ENV_PROXY=1 node -e "$JS_HTTP"
probe node-noflag node -e "$JS_FETCH"
probe bun-fetch bun -e "$JS_FETCH"
probe curl curl -sS -m 3 "$URL"
done
echo "No proxy variable at all, NO_PROXY='[::1]':"
env -i NO_PROXY='[::1]' "$PY" -c 'import httpx; httpx.Client()' 2>&1 | tail -n 1Output: Thanks again! Your diagnosis is spot on and the tests are careful, and most of this carries straight over to the shared list. If anything here is unclear, or you'd rather keep the fix narrower for a reason I've missed, just reply here. I'm happy to look at the next round. |
|
codeg work task |
A standalone bracketed IPv6 entry anywhere in NO_PROXY makes Python's HTTPX fail while it builds a default client (`Invalid port: ':1]'`), so every HTTPX-based client inheriting codeg's proxy environment breaks whatever it was about to connect to. `[::1]` came from codeg's own loopback list, and that list reaches agent launches of every distribution type, the built-in terminal and the commands agents run through ACP terminal/create — not just Antigravity — so drop it where the list is built and keep one source of truth. An exported list also carries standalone bracketed IPv6 entries bare, so a list a user typed cannot bring the failure back; URLs, domains and bracketed non-addresses keep their spelling, and the settings page still shows the entry as written. The settings hint no longer promises that the local addresses always connect directly.
8e06a19 to
4ceabdb
Compare
Launching an agent with a proxy puts
NO_PROXY=no_proxy=localhost,127.0.0.1,::1,[::1]into its environment. Python clients that read it fail before they send anything: HTTPX parses the bracketed entry asall://*[::1], takes:1]for a port and raisesInvalid port: ':1]'while it builds a default client — sohttpx.Client(), and every SDK built on it (OpenAI, Anthropic, MCP), breaks whatever it was about to connect to. The Perplexity MCP server reports exactly that and exits with EOF.[::1]comes from codeg's own loopback list, and that list reaches agent launches of every distribution type, the built-in terminal and the commands agents run through ACPterminal/create— not only Antigravity. Dropping the bracketed entry where the list is built takes it out of all of those paths at once, and keeps one source of truth.What it costs is narrow: a URL that names the IPv6 loopback literal, e.g.
http://[::1]:3000, is sent to the proxy byurllib, Bun'sfetchand Node's env proxy, whilerequests, curl and Node'shttpstill reach it directly. The loopback traffic behind #804 is IPv4 (OpenCode is launched with--hostname 127.0.0.1; Antigravity dialsws://127.0.0.1:<port>), and the settings page already documents the same three local addresses.Exported lists are normalized as well, so the failure cannot come back through the settings: a standalone bracketed IPv6 entry a user typed, e.g.
[fd00::1], is exported bare, while URLs (http://[::1]:8000) and bracketed non-addresses ([example.com]) keep their spelling. Aliases are deduplicated, and the settings page still shows the list as it was typed. The bypass hint no longer promises that the local addresses always connect directly, in all ten locales.Regression coverage: both environment-variable spellings, IPv6 alias deduplication, URL/domain preservation, empty and wildcard values, the exported settings list with a bracketed entry, and the existing credential policy.
cargo test --no-default-features --features server-bin --libpasses here fornetwork::proxy,commands::system_settings,a_proxied_launch_env_carries_the_loopback_exceptionand theantigravity_env_policycases; server mode is the only feature set this machine can build, so CI carries the desktop jobs, Clippy and the frontend checks.Reworked after @xintaofei's review: the fix now lives where the list is built rather than in Antigravity's launch policy.