xai-org/grok-build · error
Failed to connect to proxy at {proxy_addr}: {e}
Error message
Failed to connect to proxy at {proxy_addr}: {e} What it means
`open_connect_tunnel` throws this when the plain TCP connection to the HTTP CONNECT proxy itself fails (`TcpStream::connect(&proxy_addr)` returns an OS-level error). Before any CONNECT request is sent, the library must establish a TCP socket to the proxy host:port parsed from the proxy URL; if that socket setup fails, the underlying io error (refused, unreachable, DNS failure, timeout) is wrapped in this message. It indicates a problem reaching the proxy, not the target host.
Source
Thrown at crates/codegen/xai-grok-shell/src/agent/proxy.rs:151
/// 1. Parse the proxy URL to get host + port.
/// 2. Open a plain TCP connection to the proxy.
/// 3. Send `CONNECT target_host:target_port HTTP/1.1\r\n\r\n`.
/// 4. Read the proxy's response; expect `HTTP/1.x 200 …`.
/// 5. Return the raw `TcpStream` positioned after the CONNECT response.
async fn open_connect_tunnel(
proxy_url: &str,
target_host: &str,
target_port: u16,
) -> anyhow::Result<TcpStream> {
// 1. Parse proxy URL.
let (proxy_host, proxy_port) = parse_proxy_url(proxy_url)?;
// 2. TCP connect to proxy.
let proxy_addr = format!("{proxy_host}:{proxy_port}");
debug!(proxy_addr = %proxy_addr, "Opening TCP to proxy");
let stream = TcpStream::connect(&proxy_addr)
.await
.map_err(|e| anyhow::anyhow!("Failed to connect to proxy at {proxy_addr}: {e}"))?;
// 3. Send HTTP CONNECT.
let connect_req = format!(
"CONNECT {target_host}:{target_port} HTTP/1.1\r\n\
Host: {target_host}:{target_port}\r\n\
\r\n"
);
let (reader_half, mut writer_half) = stream.into_split();
writer_half.write_all(connect_req.as_bytes()).await?;
writer_half.flush().await?;
// 4. Read the status line from the proxy.
let mut reader = BufReader::new(reader_half);
let mut status_line = String::new();
reader.read_line(&mut status_line).await?;
debug!(status_line = %status_line.trim(), "Proxy CONNECT response");
if !status_line.starts_with("HTTP/1.1 200") && !status_line.starts_with("HTTP/1.0 200") {View on GitHub (pinned to bc7f02eddd)
Solutions
- Verify the proxy env vars: echo $HTTPS_PROXY/$HTTP_PROXY and confirm host and port are correct and reachable (e.g. `nc -vz <proxy_host> <proxy_port>` or `curl -x $HTTPS_PROXY https://api.example.com`)
- Check DNS/VPN: ensure the proxy hostname resolves (`getent hosts <proxy_host>`) and you are on the network (VPN) that can reach it
- Add the target host to NO_PROXY if the target is actually reachable directly and the proxy should be bypassed
- Confirm the proxy service is running and the port matches (the parser defaults to port 80 when the URL has no port — add an explicit `:port` if your proxy listens elsewhere)
- If the proxy expects credentials or a TLS-wrapped proxy connection, note this module only supports plain-HTTP CONNECT proxies; use an HTTP proxy endpoint
Example fix
// before (stale proxy in env) export HTTPS_PROXY=http://old-proxy.corp.example:3128 // after (corrected, reachable proxy) export HTTPS_PROXY=http://proxy.corp.example:3140 export NO_PROXY=localhost,127.0.0.1,.internal.example
Defensive patterns
Strategy: validation
Validate before calling
// Before connecting, validate the proxy env and reachability
fn validate_proxy(proxy_url: &str) -> anyhow::Result<()> {
let url = proxy_url.trim().to_string();
anyhow::ensure!(!url.is_empty(), "proxy URL is empty");
let authority = url.trim_start_matches("http://").trim_start_matches("https://");
let authority = authority.split('/').next().unwrap_or(authority);
let (host, port) = authority
.rsplit_once(':')
.map(|(h, p)| Ok::<_, anyhow::Error>((h.to_string(), p.parse::<u16>()?)))
.unwrap_or_else(|| Ok((authority.to_string(), 80)))?;
anyhow::ensure!(!host.is_empty(), "proxy host is empty in '{proxy_url}'");
Ok(())
}
// Optionally pre-check reachability: std::net::TcpStream::connect((host.as_str(), port)) Try / catch
match connect_via_proxy(&proxy_url, host, 443).await {
Err(e) if e.to_string().starts_with("Failed to connect to proxy") => {
eprintln!("Proxy unreachable at '{proxy_url}': check HTTPS_PROXY/HTTP_PROXY and VPN");
// fall back to direct connection if NO_PROXY policy allows
}
r => r?,
} Prevention
- Keep HTTPS_PROXY/HTTP_PROXY accurate when the corporate proxy host/port changes
- Use NO_PROXY for internal/directly-reachable hosts
- Pre-validate the proxy with a cheap TCP probe (or curl -x) before starting long-lived WebSocket sessions
- Remember the parser defaults to port 80 when no port is given — always include :port
- Verify VPN/network reachability of internal proxy hostnames
When it happens
Trigger: Calling `connect_via_proxy` (directly or via WebSocket connect when HTTPS_PROXY/HTTP_PROXY is set) where the proxy host is unreachable: nothing is listening on the proxy port, wrong proxy URL in the environment, DNS cannot resolve the proxy hostname, a firewall drops packets, or the proxy port in `HTTPS_PROXY`/`HTTP_PROXY` is stale or mistyped.
Common situations: Corporate proxy URL changed (e.g. port moved from 3128 to 3140) but HTTPS_PROXY still has the old value; running outside the corporate VPN so the internal proxy hostname does not resolve; typo like `http://proxy.corp.example:8080` with wrong port; proxy service down; using `https://` scheme in the proxy URL when the proxy only serves plain HTTP on that port.
Related errors
- Proxy CONNECT failed: {}
- bind 127.0.0.1:{port}: {e}
- send failed: {body}
- screen query failed: {body}
- resize failed: {body}
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/0bef2fc30a6d5172.
Report an issue: GitHub.