xai-org/grok-build · error

Invalid TLS server name '{server_name}': {e}

Error message

Invalid TLS server name '{server_name}': {e}

What it means

`tls_wrap` throws this when `rustls::pki_types::ServerName::try_from(server_name)` fails to parse the target hostname into a valid TLS server name. rustls only accepts well-formed DNS names, IP addresses, or exact forms; names with invalid characters, empty strings, embedded whitespace, underscores in invalid positions, or other malformed input are rejected before any handshake is attempted. This is a pre-handshake validation error in `connect_via_proxy` — the tunnel may be fine, but the name cannot be used for the TLS ClientHello/certificate verification.

Source

Thrown at crates/codegen/xai-grok-shell/src/agent/proxy.rs:206

    if !remaining.is_empty() {
        anyhow::bail!(
            "Proxy sent {} unexpected byte(s) after CONNECT response headers",
            remaining.len()
        );
    }

    // 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

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Pass only the bare hostname (no scheme, no port, no trailing slash) as `target_host` to `connect_via_proxy` — strip these before calling
  2. Validate the hostname before connecting (e.g. check it is non-empty and matches a DNS-name pattern, or run it through `rustls::pki_types::ServerName::try_from` yourself and surface a clear config error)
  3. Convert internationalized names to punycode (IDNA) before passing them
  4. Trim whitespace from config-sourced hostnames

Example fix

// before
let host = "https://api.example.com/";
connect_via_proxy(&proxy, host, 443).await?;
// after
let host = url.parse::<url::Url>()?.host_str().unwrap_or_default().trim().to_string();
connect_via_proxy(&proxy, &host, 443).await?;
Defensive patterns

Strategy: validation

Validate before calling

fn validate_tls_server_name(host: &str) -> Result<String, String> {
    let name = host.trim();
    if name.is_empty() {
        return Err("target host is empty".into());
    }
    // Reject scheme/port/path leakage — only a bare hostname/IP is valid.
    if name.contains("//") || name.contains('/') || name.contains(':') || name.contains(' ') {
        return Err(format!("'{host}' is not a bare hostname (strip scheme/port/path)"));
    }
    rustls::pki_types::ServerName::try_from(name.to_string())
        .map(|_| name.to_string())
        .map_err(|e| format!("invalid TLS server name '{name}': {e}"))
}

Type guard

fn is_valid_server_name(name: &str) -> bool {
    rustls::pki_types::ServerName::try_from(name.trim().to_string()).is_ok()
}

Try / catch

match tls_wrap(stream, server_name).await {
    Err(e) if e.to_string().starts_with("Invalid TLS server name") => {
        eprintln!("Fix target_host: must be a bare DNS name or IP, got '{server_name}'");
    }
    r => r?,
}

Prevention

When it happens

Trigger: `connect_via_proxy(proxy_url, target_host, target_port)` is called with a `target_host` that is not a valid rustls `ServerName`: empty string, uppercase-with-invalid-chars, a URL instead of a bare hostname (e.g. `https://api.example.com/` passed as host), an IDN in raw Unicode form rather than punycode, or trailing whitespace.

Common situations: Passing a full URL or `host:port` string where only the bare hostname should go; extracting the host from a config with surrounding whitespace; internationalized domain names not converted to ASCII punycode; using an IP-literal with a zone id (e.g. `fe80::1%eth0`) that rustls rejects.

Understand the failure class

Related errors


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