xai-org/grok-build · error
TLS handshake through proxy failed: {e}
Error message
TLS handshake through proxy failed: {e} What it means
`tls_wrap` throws this when the rustls TLS handshake over the established CONNECT tunnel fails (`connector.connect(dns_name, stream)` returns an error). The TCP tunnel to the target through the proxy succeeded, but the TLS layer could not complete: the server presented an untrusted/self-signed certificate, the certificate does not match `server_name`, a TLS version/cipher mismatch occurred, or the peer closed the connection during the handshake. The certificate trust chain comes from `xai_grok_extra_ca::rustls_client_config()` (native roots plus extra CAs).
Source
Thrown at crates/codegen/xai-grok-shell/src/agent/proxy.rs:211
}
// 6. Reunite the split halves back into a TcpStream.
let stream = reader.into_inner().reunite(writer_half)?;
Ok(stream)
}
async fn tls_wrap(
stream: TcpStream,
server_name: &str,
) -> anyhow::Result<tokio_rustls::client::TlsStream<TcpStream>> {
let connector = tokio_rustls::TlsConnector::from(xai_grok_extra_ca::rustls_client_config());
let dns_name = rustls::pki_types::ServerName::try_from(server_name.to_string())
.map_err(|e| anyhow::anyhow!("Invalid TLS server name '{server_name}': {e}"))?;
let tls_stream = connector
.connect(dns_name, stream)
.await
.map_err(|e| anyhow::anyhow!("TLS handshake through proxy failed: {e}"))?;
Ok(tls_stream)
}
/// Parse a proxy URL into (host, port).
///
/// Accepted formats:
/// - `http://host:port`
/// - `http://host` (defaults to port 80)
/// - `host:port`
fn parse_proxy_url(url: &str) -> anyhow::Result<(String, u16)> {
// Strip scheme if present.
let without_scheme = url
.strip_prefix("http://")
.or_else(|| url.strip_prefix("https://"))
.unwrap_or(url);
// Strip trailing path/slash.View on GitHub (pinned to bc7f02eddd)
Solutions
- Install the corporate/intercepting proxy's root CA into the system trust store so `xai_grok_extra_ca`'s native-root config picks it up (or add it to the extra-CA bundle)
- Verify the certificate with `openssl s_client -connect <host>:443 -servername <host>` — check chain, expiry, and SAN match against `target_host`
- Confirm the proxy is tunneling (CONNECT 200) rather than MITM-ing/rejecting; check proxy logs, or add the target to NO_PROXY and test a direct connection
- Ensure `target_host` matches the certificate's DNS name (an IP or wrong hostname causes verification failure)
- Check for TLS-version/cipher mismatch (very old/new servers) and network devices resetting the connection mid-handshake
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight: check the target's certificate is acceptable before opening the tunnel // openssl s_client -connect host:443 -servername host </dev/null 2>/dev/null | \ // openssl x509 -noout -subject -issuer -dates // Ensure the issuer is your corporate root (if TLS-inspected) and SANs cover the host.
Try / catch
match connect_via_proxy(&proxy_url, host, 443).await {
Err(e) if e.to_string().starts_with("TLS handshake through proxy failed") => {
eprintln!("TLS to '{host}' failed via proxy: is a TLS-inspecting proxy re-signing traffic? Install its root CA.");
// optionally retry once after CA refresh, or bypass proxy via NO_PROXY
}
r => r?,
} Prevention
- Install corporate MITM/inspection proxy root CAs into the system trust store used by rustls
- Monitor certificate expiry for target hosts
- Confirm the CONNECT tunnel is truly end-to-end (proxy not intercepting) for wss targets
- Ensure target_host exactly matches the certificate SAN
- Test with openssl s_client when adding new target hosts
When it happens
Trigger: After a successful CONNECT through the proxy, performing the TLS handshake with the target host fails: target presents a certificate not signed by a trusted root (corporate MITM proxy re-signing TLS whose CA is not installed), hostname/certificate mismatch, proxy intercepting and resetting the connection, or the target speaking plain HTTP where wss/TLS was expected.
Common situations: Corporate TLS-inspection proxy (Zscaler, Netskope, Palo Alto) re-signing traffic with an internal CA not present in the native trust store; connecting to a host whose cert expired or covers a different name; a proxy that terminates TLS itself and rejects CONNECT tunneling; firewalls resetting long handshakes; pointing at a plain-HTTP endpoint on port 443.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Invalid TLS server name '{server_name}': {e}
- Proxy sent {} unexpected byte(s) after CONNECT response head
- aws-lc-rs supports the default protocol versions
- Proxy CONNECT failed: {}
- Failed to connect to proxy at {proxy_addr}: {e}
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/0201dc6ae0a523a4.
Report an issue: GitHub.