{"record":{"id":"309a30666cc1c3ca","repo":"PrefectHQ/fastmcp","slug":"requested-scopes-are-not-valid-join-invalid","errorCode":null,"errorMessage":"Requested scopes are not valid: {', '.join(invalid_scopes)}","messagePattern":"Requested scopes are not valid: (.+?)","errorType":"error_code","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/auth/providers/in_memory.py","lineNumber":84,"sourceCode":"        self._refresh_to_access_map: dict[\n            str, str\n        ] = {}  # refresh_token_str -> access_token_str\n\n    async def get_client(self, client_id: str) -> OAuthClientInformationFull | None:\n        return self.clients.get(client_id)\n\n    async def register_client(self, client_info: OAuthClientInformationFull) -> None:\n        # Validate scopes against valid_scopes if configured (matches MCP SDK behavior)\n        if (\n            client_info.scope is not None\n            and self.client_registration_options is not None\n            and self.client_registration_options.valid_scopes is not None\n        ):\n            requested_scopes = set(client_info.scope.split())\n            valid_scopes = set(self.client_registration_options.valid_scopes)\n            invalid_scopes = requested_scopes - valid_scopes\n            if invalid_scopes:\n                raise ValueError(\n                    f\"Requested scopes are not valid: {', '.join(invalid_scopes)}\"\n                )\n\n        if client_info.client_id is None:\n            raise ValueError(\"client_id is required for client registration\")\n        if client_info.client_id in self.clients:\n            # As per RFC 7591, if client_id is already known, it's an update.\n            # For this simple provider, we'll treat it as re-registration.\n            # A real provider might handle updates or raise errors for conflicts.\n            pass\n        self.clients[client_info.client_id] = client_info\n\n    async def authorize(\n        self, client: OAuthClientInformationFull, params: AuthorizationParams\n    ) -> str:\n        \"\"\"\n        Simulates user authorization and generates an authorization code.\n        Returns a redirect URI with the code and state.","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/auth/providers/in_memory.py#L66-L102","documentation":"The in-memory OAuth provider validates that scopes requested during dynamic client registration are a subset of the provider's configured valid_scopes. If any requested scope is outside that set, register_client raises this ValueError listing the offending scopes.","triggerScenarios":"Calling register_client(client_info) where client_info.scope contains one or more space-separated scopes not present in client_registration_options.valid_scopes (which must also be configured for the check to run).","commonSituations":"A client app requests scopes like 'read write admin' while the provider was configured with valid_scopes=['read', 'write']; typos in scope names ('read:profile' vs 'read_profile'); valid_scopes tightened after clients were built.","solutions":["Remove or fix the invalid scopes in the OAuthClientMetadata scope string sent to register_client","Add the missing scopes to client_registration_options.valid_scopes when instantiating the in-memory provider","Print the diff: set(requested.split()) - set(valid_scopes) to see exactly which scopes are rejected"],"exampleFix":"// before\nprovider = InMemoryOAuthProvider(client_registration_options=ClientRegistrationOptions(valid_scopes=[\"read\", \"write\"]))\nawait provider.register_client(OAuthClientMetadata(scope=\"read write admin\"))\n// after\nawait provider.register_client(OAuthClientMetadata(scope=\"read write\"))  # drop 'admin'\n# or widen: valid_scopes=[\"read\", \"write\", \"admin\"]","handlingStrategy":"validation","validationCode":"valid = set(provider.client_registration_options.valid_scopes or [])\nrequested = set(client_scope_string.split())\nif invalid := requested - valid:\n    raise ValueError(f\"Adjust client scopes; not allowed: {', '.join(invalid)}\")","typeGuard":"def scopes_allowed(scope_string: str | None, valid_scopes: list[str] | None) -> bool:\n    if scope_string is None or valid_scopes is None:\n        return True\n    return set(scope_string.split()) <= set(valid_scopes)","tryCatchPattern":"try:\n    await provider.register_client(client_info)\nexcept ValueError as e:\n    # message lists offending scopes; trim requested scopes and retry\n    logger.warning(\"Registration rejected: %s\", e)","preventionTips":["Keep a single shared constant of allowed scopes used by both provider config and clients","Validate client scope strings in CI against the provider's valid_scopes","Watch for typo'd scope separators (spaces required, not commas)"],"tags":["oauth","scopes","validation"],"backgroundTag":"invalid-oauth-scope","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}