PrefectHQ/fastmcp · error · ValueError

Requested scopes are not valid: {', '.join(invalid_scopes)}

Error message

Requested scopes are not valid: {', '.join(invalid_scopes)}

What it means

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.

Source

Thrown at fastmcp_slim/fastmcp/server/auth/providers/in_memory.py:84

        self._refresh_to_access_map: dict[
            str, str
        ] = {}  # refresh_token_str -> access_token_str

    async def get_client(self, client_id: str) -> OAuthClientInformationFull | None:
        return self.clients.get(client_id)

    async def register_client(self, client_info: OAuthClientInformationFull) -> None:
        # Validate scopes against valid_scopes if configured (matches MCP SDK behavior)
        if (
            client_info.scope is not None
            and self.client_registration_options is not None
            and self.client_registration_options.valid_scopes is not None
        ):
            requested_scopes = set(client_info.scope.split())
            valid_scopes = set(self.client_registration_options.valid_scopes)
            invalid_scopes = requested_scopes - valid_scopes
            if invalid_scopes:
                raise ValueError(
                    f"Requested scopes are not valid: {', '.join(invalid_scopes)}"
                )

        if client_info.client_id is None:
            raise ValueError("client_id is required for client registration")
        if client_info.client_id in self.clients:
            # As per RFC 7591, if client_id is already known, it's an update.
            # For this simple provider, we'll treat it as re-registration.
            # A real provider might handle updates or raise errors for conflicts.
            pass
        self.clients[client_info.client_id] = client_info

    async def authorize(
        self, client: OAuthClientInformationFull, params: AuthorizationParams
    ) -> str:
        """
        Simulates user authorization and generates an authorization code.
        Returns a redirect URI with the code and state.

View on GitHub (pinned to 1f02114297)

Solutions

  1. Remove or fix the invalid scopes in the OAuthClientMetadata scope string sent to register_client
  2. Add the missing scopes to client_registration_options.valid_scopes when instantiating the in-memory provider
  3. Print the diff: set(requested.split()) - set(valid_scopes) to see exactly which scopes are rejected

Example fix

// before
provider = InMemoryOAuthProvider(client_registration_options=ClientRegistrationOptions(valid_scopes=["read", "write"]))
await provider.register_client(OAuthClientMetadata(scope="read write admin"))
// after
await provider.register_client(OAuthClientMetadata(scope="read write"))  # drop 'admin'
# or widen: valid_scopes=["read", "write", "admin"]
Defensive patterns

Strategy: validation

Validate before calling

valid = set(provider.client_registration_options.valid_scopes or [])
requested = set(client_scope_string.split())
if invalid := requested - valid:
    raise ValueError(f"Adjust client scopes; not allowed: {', '.join(invalid)}")

Type guard

def scopes_allowed(scope_string: str | None, valid_scopes: list[str] | None) -> bool:
    if scope_string is None or valid_scopes is None:
        return True
    return set(scope_string.split()) <= set(valid_scopes)

Try / catch

try:
    await provider.register_client(client_info)
except ValueError as e:
    # message lists offending scopes; trim requested scopes and retry
    logger.warning("Registration rejected: %s", e)

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


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