PrefectHQ/fastmcp · error · RuntimeError

No authorization response stored. redirect_handler must be c

Error message

No authorization response stored. redirect_handler must be called first.

What it means

This test OAuth helper stores the redirect response when redirect_handler is called; callback_handler parses that stored response. If callback_handler runs before any redirect was captured (self._stored_response is falsy), it raises RuntimeError explaining that redirect_handler must run first. It enforces the OAuth redirect-then-callback ordering.

Source

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

    This simulates the complete OAuth flow programmatically by making HTTP requests
    instead of opening a browser and running a callback server. Useful for automated testing.
    """

    def __init__(self, mcp_url: str, **kwargs):
        """Initialize HeadlessOAuth with stored response tracking."""
        self._stored_response = None
        super().__init__(mcp_url, **kwargs)

    async def redirect_handler(self, authorization_url: str) -> None:
        """Make HTTP request to authorization URL and store response for callback handler."""
        async with httpx2.AsyncClient() as client:
            response = await client.get(authorization_url, follow_redirects=False)
            self._stored_response = response

    async def callback_handler(self) -> AuthorizationCodeResult:
        """Parse stored response and return the authorization code result."""
        if not self._stored_response:
            raise RuntimeError(
                "No authorization response stored. redirect_handler must be called first."
            )

        response = self._stored_response

        # Extract auth code from redirect location
        if response.status_code == 302:
            redirect_url = response.headers["location"]
            parsed = urlparse(redirect_url)
            # 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]

View on GitHub (pinned to 1f02114297)

Solutions

  1. Ensure redirect_handler is awaited/called with the authorization URL before callback_handler
  2. Verify the client is configured to use this helper as its redirect handler so the response gets stored
  3. Check that the same helper instance is used for both redirect and callback steps
  4. Inspect the authorization flow earlier — if the client never redirects, the OAuth config (client_id, redirect URI) may be wrong

Example fix

// before
result = await helper.callback_handler()  # nothing stored yet

// after
await helper.redirect_handler(authorization_url)
result = await helper.callback_handler()
Defensive patterns

Strategy: validation

Validate before calling

if not getattr(helper, "_stored_response", None):
    raise RuntimeError("redirect_handler must be called before callback_handler")

Try / catch

try:
    result = await helper.callback_handler()
except RuntimeError as e:
    if "No authorization response" in str(e):
        raise AssertionError("OAuth flow never redirected; check redirect_handler wiring") from e
    raise

Prevention

When it happens

Trigger: Calling callback_handler() before redirect_handler() on the same helper instance; redirect_handler never being invoked because the client never got redirected; a new helper instance being used for the callback; the stored response being cleared between steps.

Common situations: Hand-rolled OAuth test flows where the authorization step is skipped or fails silently before redirect; miswired redirect handler so the browser/client request never reaches it; reusing helpers incorrectly across client sessions.

Related errors


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