Skip to content

fix(httpclient): treat HTML responses as AuthRequiredErr in readResponse#293

Open
mmailhos wants to merge 1 commit into
obot-platform:mainfrom
mmailhos:fix/html-response-triggers-reauth
Open

fix(httpclient): treat HTML responses as AuthRequiredErr in readResponse#293
mmailhos wants to merge 1 commit into
obot-platform:mainfrom
mmailhos:fix/html-response-triggers-reauth

Conversation

@mmailhos

Copy link
Copy Markdown

Problem

When an upstream MCP server's OAuth access token expires, the server may return an HTML login-redirect page (HTTP 200 after following a 3xx, or the redirect body itself) instead of a JSON response.

readResponse currently returns a generic "invalid response format" error for any non-{ body. This error is not caught by either the AuthRequiredErr or oauth2: handlers in Send(), so instead of triggering the OAuth re-authentication flow, the error surfaces to the caller as:

failed to decode response: invalid character '<' looking for beginning of value

Root cause

Send() has two recovery paths for auth failures:

  1. errors.AsType[AuthRequiredErr](err) — deletes the stored token and re-runs the OAuth flow
  2. strings.HasPrefix(err, "oauth2:") — resets to http.DefaultClient and retries (which triggers path 1)

Neither matches the plain "invalid response format" string returned by readResponse for HTML bodies, so both paths are skipped and the error propagates as-is.

Fix

Before reading the body:

  • Check Content-Type: text/html → return AuthRequiredErr immediately (discarding the body)

After ReadAll:

  • If the first byte is <, the body is almost certainly HTML from an auth redirect → return AuthRequiredErr

This makes Send() correctly trigger the OAuth re-authentication flow when a stored upstream token expires.

Reproduction

Observed with Amplitude MCP (https://mcp.amplitude.com/mcp) used as a remote server behind the Obot gateway. After the initial OAuth connection, the stored Amplitude access token expires. On the next MCP request, Amplitude responds with an HTML redirect to its login page. With this fix, readResponse returns AuthRequiredErr, Send() deletes the stale token and re-runs the OAuth flow transparently.

Testing

  • go build ./pkg/mcp/... passes
  • Manually verified the error path by inspecting the Obot gateway logs showing "failed to decode response: invalid character '<' looking for beginning of value" disappears after applying the fix

Made with Cursor

When an upstream MCP server's OAuth token expires, the server may return
an HTML login-redirect page (status 200 or 3xx-followed) instead of JSON.
readResponse currently returns a generic "invalid response format" error
for any non-JSON body, which is not caught by the AuthRequiredErr or
oauth2: handlers in Send(), so the error surfaces as a confusing
"failed to decode response: invalid character '<'" instead of triggering
the OAuth re-authentication flow.

Fix: before reading the body, check Content-Type for text/html and return
AuthRequiredErr. As a defence-in-depth measure, also check if the body
starts with '<' after ReadAll, which catches cases where the Content-Type
header is missing but the body is clearly HTML.

This makes the OAuth re-authentication flow trigger correctly when a
stored upstream token expires mid-session (e.g. Amplitude MCP).

Made-with: Cursor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant