shadowsocks/shadowsocks-rust · error

unexpected response from http://clients3.google.com/generate

Error message

unexpected response from http://clients3.google.com/generate_204, {:?}

What it means

In shadowsocks-service's ping_balancer, check_request_tcp_chromium sends an HTTP request to http://clients3.google.com/generate_204 through a candidate proxy server and expects a 204 response proving the server works. If httparse cannot parse the reply or the status code is not 204, the server (or the path through it) is broken, so InvalidData is thrown with the raw response bytes in the message for diagnosis.

Source

Thrown at crates/shadowsocks-service/src/local/loadbalancing/ping_balancer.rs:875

            &addr,
            self.server.connect_opts_ref(),
        )
        .await?;
        stream.write_all(GET_BODY).await?;

        let mut reader = BufReader::new(stream);

        let mut buf = Vec::new();
        reader.read_until(b'\n', &mut buf).await?;

        let mut headers = [httparse::EMPTY_HEADER; 1];
        let mut response = httparse::Response::new(&mut headers);

        if response.parse(&buf).is_ok() && matches!(response.code, Some(204)) {
            return Ok(());
        }

        Err(Error::new(
            ErrorKind::InvalidData,
            format!(
                "unexpected response from http://clients3.google.com/generate_204, {:?}",
                ByteStr::new(&buf)
            ),
        ))
    }

    /// Detect TCP connectivity with Firefox's http://detectportal.firefox.com/success.txt
    async fn check_request_tcp_firefox(&self) -> io::Result<()> {
        use std::io::{Error, ErrorKind};

        const GET_BODY: &[u8] =
            b"GET /success.txt HTTP/1.1\r\nHost: detectportal.firefox.com\r\nConnection: close\r\nAccept: */*\r\n\r\n";

        let addr = Address::DomainNameAddress("detectportal.firefox.com".to_owned(), 80);

        let mut stream = ProxyClientStream::connect_with_opts(

View on GitHub (pinned to 8eb0f0a65b)

Solutions

  1. Check whether the proxy server actually relays traffic (test with curl through the same proxy).
  2. Switch to another server in the balancer or update the server list; this node is failing the liveness probe.
  3. Verify no local firewall/ISP interception is rewriting HTTP responses (test generate_204 directly).
  4. If the target is blocked in your network, point the probe at a reachable URL by adjusting the balancer's check logic.

Example fix

// before: server fails the probe silently or is removed
let balancer = PingBalancerBuilder::new(check_adapter, mode).build().await;
// after: handle probe failures gracefully by scheduling re-check instead of treating as fatal
match check_request(server).await {
    Ok(()) => balancer.add_server(server, estimate),
    Err(err) => tracing::warn!(?err, "server failed connectivity check, rescheduling"),
}
Defensive patterns

Strategy: retry

Validate before calling

// precheck: only feed servers into the balancer that can reach the probe
tcping(server_addr, Duration::from_secs(3)).await.is_ok()

Try / catch

match check_request(server).await {
    Ok(()) => balancer.add_server(server, estimate),
    Err(e) if e.kind() == std::io::ErrorKind::InvalidData => {
        tracing::warn!("probe failed (unexpected response), rescheduling check: {e}");
        schedule_recheck(server);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Running check_request_tcp_chromium (via loadbalancer's check_request) when the proxy server returns garbage, an HTTP error page, a captive-portal redirect (302/200), or closes/truncates the connection instead of returning HTTP 204 from the generate_204 endpoint.

Common situations: Dead or censored proxy nodes, captive portals intercepting traffic, GFW-style hijacking returning reset/redirect pages, or a node that resolves generate_204 incorrectly. Commonly surfaced during network-change events or periodic server health checks.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09). Data as JSON: /api/errors/cc8345b57364058a. Report an issue: GitHub.