siyuan-note/siyuan · error

proxy returned unexpected tunnel data

Error message

proxy returned unexpected tunnel data

What it means

In this SSRF-safe transport's RoundTrip, after the proxy answers CONNECT for an https target, the code checks that the proxy sent exactly the tunnel establishment bytes: any buffered residue means the proxy injected or pipelined data into the tunnel before TLS began. The connection is closed because proceeding could leak or corrupt the TLS stream (e.g. a MITM proxy that already spoke plaintext).

Source

Thrown at kernel/util/httprequest.go:109

		return nil, err
	}
	if proxyURL == nil {
		return t.directTransport.RoundTrip(req)
	}

	targetAddr, err := t.resolvePublicTarget(req.Context(), req.URL)
	if err != nil {
		return nil, err
	}
	conn, reader, err := dialProxyTunnel(req.Context(), proxyURL, targetAddr)
	if err != nil {
		return nil, err
	}

	if req.URL.Scheme == "https" {
		if reader.Buffered() != 0 {
			conn.Close()
			return nil, errors.New("proxy returned unexpected tunnel data")
		}
		tlsConn := tls.Client(conn, &tls.Config{ServerName: req.URL.Hostname(), NextProtos: []string{"http/1.1"}})
		if err = tlsConn.HandshakeContext(req.Context()); err != nil {
			conn.Close()
			return nil, err
		}
		conn = tlsConn
		reader = bufio.NewReader(conn)
	}

	targetReq := req.Clone(req.Context())
	targetReq.URL = cloneURL(req.URL)
	targetReq.URL.Scheme = ""
	targetReq.URL.Host = ""
	targetReq.RequestURI = ""
	targetReq.Header.Del("Proxy-Authorization")
	if targetReq.Host == "" {
		targetReq.Host = req.URL.Host

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Inspect/replace the HTTP proxy configured for the agent client — use a standards-compliant proxy that sends only the CONNECT response with no residual bytes
  2. Test the proxy with 'curl -x proxy https://target' to see if it injects data; update or bypass the intercepting proxy for this traffic
  3. If the target can be reached directly, remove the proxy setting so RoundTrip uses the direct SSRF-validated connection

Example fix

// before: intercepting proxy injects bytes after CONNECT
HTTPS_PROXY=http://corporate-mitm:8080
// after: allowlist the target on the proxy or connect directly
HTTPS_PROXY=
Defensive patterns

Strategy: fallback

Try / catch

if err := client.Do(req); err != nil {
    if strings.Contains(err.Error(), "proxy returned unexpected tunnel data") {
        return directFallback(req) // retry without the proxy
    }
    return err
}

Prevention

When it happens

Trigger: RoundTrip routes an https request through ssrfSafeClient's proxy path; after reading the CONNECT response, bufio reader has leftover bytes (proxy sent 200 followed by extra data, or responded non-protocol data).

Common situations: Corporate/intercepting proxies (Zscaler, squid with injected headers) that append data after the CONNECT response; misbehaving custom forward proxies; a proxy answering CONNECT with an error page plus keep-alive residue.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/d49d49a64ddd04c5. Report an issue: GitHub.