{"record":{"id":"0c57c65f3b9afb87","repo":"PrefectHQ/fastmcp","slug":"oauth-authorization-failed-error-error-desc","errorCode":null,"errorMessage":"OAuth authorization failed: {error} - {error_desc}","messagePattern":"OAuth authorization failed: (.+?) - (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/utilities/tests.py","lineNumber":505,"sourceCode":"                \"No authorization response stored. redirect_handler must be called first.\"\n            )\n\n        response = self._stored_response\n\n        # Extract auth code from redirect location\n        if response.status_code == 302:\n            redirect_url = response.headers[\"location\"]\n            parsed = urlparse(redirect_url)\n            # keep_blank_values=True so explicitly-empty params (e.g. ?state=)\n            # survive parsing instead of being silently dropped. Real OAuth\n            # callbacks can include empty `state` or `error_description`,\n            # and downstream code distinguishes \"\" from missing.\n            query_params = parse_qs(parsed.query, keep_blank_values=True)\n\n            if \"error\" in query_params:\n                error = query_params[\"error\"][0]\n                error_desc = query_params.get(\"error_description\", [\"Unknown error\"])[0]\n                raise RuntimeError(\n                    f\"OAuth authorization failed: {error} - {error_desc}\"\n                )\n\n            auth_code = query_params[\"code\"][0]\n            state = query_params.get(\"state\", [None])[0]\n            iss = query_params.get(\"iss\", [None])[0]\n            return AuthorizationCodeResult(code=auth_code, state=state, iss=iss)\n        else:\n            raise RuntimeError(f\"Authorization failed: {response.status_code}\")\n","sourceCodeStart":487,"sourceCodeEnd":515,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/utilities/tests.py#L487-L515","documentation":"When the OAuth provider redirects back with an `error` query parameter instead of a `code`, callback_handler raises RuntimeError containing the error code and description from the provider (e.g. access_denied, invalid_client). This surfaces authorization failures that happened on the provider side.","triggerScenarios":"The authorization server returns error=...&error_description=... to the redirect URI — user denied consent, client_id/redirect_uri rejected, scope invalid, or account selection failed. callback_handler parses the query params and sees 'error' present.","commonSituations":"Misconfigured client_id or redirect URI on the provider dashboard; user cancelling the consent screen in tests; requesting scopes the app is not allowed to; expired/rotated client secrets in test fixtures.","solutions":["Read the error/error_description in the exception message and fix the corresponding OAuth client configuration","Check client_id, redirect_uri, and scope match the provider's registered values","For access_denied in tests, automate consent or use a test user that pre-approves the app","Verify provider credentials (secret rotation) and tenant/issuer settings"],"exampleFix":"// before\nredirect_uri = \"http://localhost:9999/callback\"  # not registered with provider\n\n// after\nredirect_uri = \"http://localhost:6274/callback\"  # registered redirect URI","handlingStrategy":"try-catch","validationCode":"params = dict(parse_qs(urlparse(auth_url).query))\nassert \"client_id\" in params and \"redirect_uri\" in params, \"malformed authorization request\"","typeGuard":null,"tryCatchPattern":"try:\n    result = await helper.callback_handler()\nexcept RuntimeError as e:\n    if \"OAuth authorization failed\" in str(e):\n        print(e)  # e.g. access_denied - The user has denied your application access\n    raise","preventionTips":["Keep client_id/redirect_uri/scope in sync with the provider dashboard","Pre-approve test users to avoid access_denied","Check error_description in the message before debugging code","Rotate secrets on schedule to avoid invalid_client"],"tags":["oauth","auth","authorization","testing"],"backgroundTag":"oauth-authorization-failed","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}