windmill-labs/windmill · error

HTTP proxy CONNECT to {} rejected: {}

Error message

HTTP proxy CONNECT to {} rejected: {}

What it means

`http_connect_tunnel` sends an HTTP `CONNECT host:port` request and then parses the proxy's response. If the status line is not a 2xx success (per the `status_ok` check), the proxy refused to open the tunnel, and the function returns the proxy's own status line (e.g. `HTTP/1.1 407 Proxy Authentication Required`) embedded in this io::Error message.

Source

Thrown at backend/windmill-trigger-websocket/src/proxy.rs:293

        ));
    }

    let status_ok = status_line
        .split_whitespace()
        .nth(1)
        .map(|s| s == "200")
        .unwrap_or(false);

    loop {
        let mut line = String::new();
        let n = reader.read_line(&mut line).await?;
        if n == 0 || line == "\r\n" || line == "\n" {
            break;
        }
    }

    if !status_ok {
        return Err(io::Error::new(
            io::ErrorKind::Other,
            format!(
                "HTTP proxy CONNECT to {} rejected: {}",
                host_header,
                status_line.trim_end()
            ),
        ));
    }

    // A conforming proxy stays silent after the CONNECT response until the
    // client speaks. If our read buffer is non-empty, the proxy spoke
    // first — handing the raw socket to TLS would silently drop those
    // bytes and break the handshake.
    if !reader.buffer().is_empty() {
        return Err(io::Error::new(
            io::ErrorKind::Other,
            "HTTP proxy sent unexpected bytes after CONNECT response",
        ));

View on GitHub (pinned to e474e8803c)

Solutions

  1. Read the status in the message: 407 → add/fix proxy credentials (user:pass), 403 → request allowlisting of the target host on the proxy, 5xx → check the proxy's outbound connectivity to the target
  2. Supply proxy credentials in the proxy URL (`http://user:pass@proxy:port`) so basic auth is forwarded
  3. Verify the target websocket host:port is reachable from the proxy host itself (e.g. `curl https://target -v` from the proxy machine)
  4. Check proxy access logs for the exact CONNECT denial reason

Example fix

// before
let proxy = "http://proxy.corp:3128"; // 407 Proxy Authentication Required
// after
let proxy = "http://user:pass@proxy.corp:3128";
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight CONNECT to catch 407/403 before the websocket call
let status = probe_proxy_connect(proxy_url, target_host, target_port).await?;
if !status.is_2xx() {
    return Err(anyhow!("proxy CONNECT preflight failed with {status}"));
}

Try / catch

match connect_async_with_proxy(&url, &proxy).await {
    Err(WsError::Io(e)) if e.to_string().contains("rejected") => {
        if e.to_string().contains("407") {
            eprintln!("proxy auth failed: embed credentials in proxy URL (http://user:pass@proxy)");
        }
        // else: log the embedded status line, check proxy ACLs/target reachability
    }
    Err(e) => return Err(e.into()),
    Ok(_) => {}
}

Prevention

When it happens

Trigger: Calling `connect_async_with_proxy` (via `test_connection` or `get_consumer`) when the HTTP proxy answers the CONNECT request with a non-2xx status — typically 407 (auth required), 403 (denied), or 502/503 (cannot reach target).

Common situations: Missing or wrong proxy credentials (407); corporate proxy blocks the destination host (403); target websocket host unreachable from the proxy's network (502/504); proxy misconfigured to require auth while client sends none.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/aa2fa52f300f0f8d. Report an issue: GitHub.