Hmbown/CodeWhale · error

MCP HTTP redirect limit exceeded

Error message

MCP HTTP redirect limit exceeded

What it means

The MCP HTTP client follows redirects manually and caps them at 5. When a server (or redirect chain) exceeds six hops, execute_inner stops and throws this error rather than looping indefinitely. It protects against redirect loops and infinite chains.

Solutions

  1. Fix the server/proxy redirect chain so the endpoint responds within 5 hops
  2. Connect directly to the final destination URL instead of following a chain
  3. Check for scheme or trailing-slash loops in reverse-proxy config (common: HTTP->HTTPS loop with HSTS off)

Example fix

// before
let client = client_for_target(...); // keeps following redirects until bail
// after
// resolve the redirect chain out-of-band and use the final URL directly
let final_url = resolve_redirects(&url).await?; // separate tooling
let client = McpHttpClient::new(final_url.as_str(), ...)?;
Defensive patterns

Strategy: try-catch

Try / catch

match client.execute(request).await {
    Err(e) if e.to_string().contains("redirect limit exceeded") => {
        // diagnose server redirect loop; optionally resolve final URL manually
    }
    other => other?,
}

Prevention

When it happens

Trigger: execute_inner receives a 3xx response with a Location header when redirect_count already equals 5 — i.e. the sixth redirect in one request chain.

Common situations: Server misconfiguration causing a redirect loop (e.g. http->https->http, or trailing-slash loops); an auth gateway bouncing between login endpoints; a proxy that rewrites redirects endlessly.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/ec35c6dca361c091. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/mcp/http_client.rs:126

        for redirect_count in 0..=5 {
            let url = request.url().clone();
            let client = self.client_for_target(&url).await?;
            // MCP and OAuth requests have buffered bodies. Keep the exact request
            // to replay only after the Location has passed the same guard.
            let next_request = request
                .try_clone()
                .context("MCP request body cannot be replayed")?;
            let response = client.execute(request).await?;
            if !follow_redirects
                || !matches!(response.status().as_u16(), 301 | 302 | 303 | 307 | 308)
            {
                return Ok(response);
            }
            let Some(location) = response.headers().get(header::LOCATION) else {
                return Ok(response);
            };
            if redirect_count == 5 {
                bail!("MCP HTTP redirect limit exceeded");
            }
            let next_url = url.join(location.to_str().context("invalid MCP redirect Location")?)?;
            validate_url(&next_url)?;
            if url_has_credentials(&next_url) {
                bail!("MCP HTTP redirect must not contain credentials");
            }
            if url.scheme() == "https" && next_url.scheme() != "https" {
                bail!("MCP HTTP redirect would downgrade HTTPS");
            }
            request = next_request;
            if (matches!(response.status().as_u16(), 301 | 302) && request.method() == Method::POST)
                || (response.status().as_u16() == 303 && request.method() != Method::HEAD)
            {
                *request.method_mut() = Method::GET;
                *request.body_mut() = None;
                request.headers_mut().remove(header::CONTENT_TYPE);
                request.headers_mut().remove(header::CONTENT_LENGTH);
                request.headers_mut().remove(header::TRANSFER_ENCODING);

View on GitHub (pinned to 73e0f67d83)