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

  1. 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`)
  2. Check DNS/VPN: ensure the proxy hostname resolves (`getent hosts <proxy_host>`) and you are on the network (VPN) that can reach it
  3. Add the target host to NO_PROXY if the target is actually reachable directly and the proxy should be bypassed
  4. 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)
  5. 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

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


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/0bef2fc30a6d5172. Report an issue: GitHub.