{"record":{"id":"0201dc6ae0a523a4","repo":"xai-org/grok-build","slug":"tls-handshake-through-proxy-failed-e","errorCode":null,"errorMessage":"TLS handshake through proxy failed: {e}","messagePattern":"TLS handshake through proxy failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-shell/src/agent/proxy.rs","lineNumber":211,"sourceCode":"    }\n\n    // 6. Reunite the split halves back into a TcpStream.\n    let stream = reader.into_inner().reunite(writer_half)?;\n    Ok(stream)\n}\n\nasync fn tls_wrap(\n    stream: TcpStream,\n    server_name: &str,\n) -> anyhow::Result<tokio_rustls::client::TlsStream<TcpStream>> {\n    let connector = tokio_rustls::TlsConnector::from(xai_grok_extra_ca::rustls_client_config());\n    let dns_name = rustls::pki_types::ServerName::try_from(server_name.to_string())\n        .map_err(|e| anyhow::anyhow!(\"Invalid TLS server name '{server_name}': {e}\"))?;\n\n    let tls_stream = connector\n        .connect(dns_name, stream)\n        .await\n        .map_err(|e| anyhow::anyhow!(\"TLS handshake through proxy failed: {e}\"))?;\n\n    Ok(tls_stream)\n}\n\n/// Parse a proxy URL into (host, port).\n///\n/// Accepted formats:\n/// - `http://host:port`\n/// - `http://host` (defaults to port 80)\n/// - `host:port`\nfn parse_proxy_url(url: &str) -> anyhow::Result<(String, u16)> {\n    // Strip scheme if present.\n    let without_scheme = url\n        .strip_prefix(\"http://\")\n        .or_else(|| url.strip_prefix(\"https://\"))\n        .unwrap_or(url);\n\n    // Strip trailing path/slash.","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-shell/src/agent/proxy.rs#L193-L229","documentation":"`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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Pre-flight: check the target's certificate is acceptable before opening the tunnel\n// openssl s_client -connect host:443 -servername host </dev/null 2>/dev/null | \\\n//   openssl x509 -noout -subject -issuer -dates\n// Ensure the issuer is your corporate root (if TLS-inspected) and SANs cover the host.","typeGuard":null,"tryCatchPattern":"match connect_via_proxy(&proxy_url, host, 443).await {\n    Err(e) if e.to_string().starts_with(\"TLS handshake through proxy failed\") => {\n        eprintln!(\"TLS to '{host}' failed via proxy: is a TLS-inspecting proxy re-signing traffic? Install its root CA.\");\n        // optionally retry once after CA refresh, or bypass proxy via NO_PROXY\n    }\n    r => r?,\n}","preventionTips":["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"],"tags":["tls","rustls","certificate","proxy","handshake"],"backgroundTag":"tls-handshake-failure","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}