PrefectHQ/fastmcp · error · RuntimeError

Authorization failed: {response.status_code}

Error message

Authorization failed: {response.status_code}

What it means

callback_handler expects the stored redirect response to be a 3xx redirect carrying the authorization code. If the response has any other status code, it raises RuntimeError with the actual status, meaning the authorization request did not produce a redirect (e.g. the provider rendered an error page).

Source

Thrown at fastmcp_slim/fastmcp/utilities/tests.py:514

            # keep_blank_values=True so explicitly-empty params (e.g. ?state=)
            # survive parsing instead of being silently dropped. Real OAuth
            # callbacks can include empty `state` or `error_description`,
            # and downstream code distinguishes "" from missing.
            query_params = parse_qs(parsed.query, keep_blank_values=True)

            if "error" in query_params:
                error = query_params["error"][0]
                error_desc = query_params.get("error_description", ["Unknown error"])[0]
                raise RuntimeError(
                    f"OAuth authorization failed: {error} - {error_desc}"
                )

            auth_code = query_params["code"][0]
            state = query_params.get("state", [None])[0]
            iss = query_params.get("iss", [None])[0]
            return AuthorizationCodeResult(code=auth_code, state=state, iss=iss)
        else:
            raise RuntimeError(f"Authorization failed: {response.status_code}")

View on GitHub (pinned to 1f02114297)

Solutions

  1. Check response.status_code in the message and fetch the response body/HTML to see the provider's error page
  2. Verify the authorization endpoint URL and query parameters (client_id, redirect_uri, scope, PKCE challenge)
  3. In tests, pre-authenticate the HTTP client/cookie jar so the provider does not return a login page
  4. Retry if the status is 5xx — the provider may be temporarily unavailable
Defensive patterns

Strategy: try-catch

Validate before calling

assert 300 <= response.status_code < 400, f"expected redirect, got {response.status_code}"

Try / catch

try:
    result = await helper.callback_handler()
except RuntimeError as e:
    if "Authorization failed" in str(e):
        status = int(str(e).rsplit(":", 1)[1])
        if status >= 500: retry_authorization()
    raise

Prevention

When it happens

Trigger: follow_redirects=False GET to the authorization URL returns 200 (login/error page), 4xx (bad request, invalid client), or 5xx (provider outage) instead of a redirect; callback_handler then hits the else branch and raises.

Common situations: Wrong authorization endpoint URL; provider requiring login so the response is an HTML sign-in page; network/proxy issues in CI returning 502/503; provider returning 400 due to malformed PKCE/state parameters.

Related errors


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/0a55dd509d9046a4. Report an issue: GitHub.