PrefectHQ/fastmcp · error · ValueError

client_id is required for client registration

Error message

client_id is required for client registration

What it means

register_client stores clients keyed by client_id, so it requires that the submitted OAuthClientMetadata actually carries a non-None client_id. RFC 7591 normally has the server mint the id, but this simple provider expects the caller to supply one; a None id cannot be a dictionary key.

Source

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

        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.
        """
        if client.client_id not in self.clients:
            raise AuthorizeError(
                error="unauthorized_client",
                error_description=f"Client '{client.client_id}' not registered.",

View on GitHub (pinned to 1f02114297)

Solutions

  1. Assign a client_id to the OAuthClientMetadata before calling register_client (e.g. generate one with secrets.token_hex(16))
  2. If simulating RFC 7591 dynamic registration, let the server generate the id before storing rather than passing empty metadata
  3. Check that whatever builds the metadata object actually sets client_id and not a similarly named field

Example fix

// before
meta = OAuthClientMetadata(redirect_uris=["http://localhost/callback"])
provider.register_client(meta)
// after
meta = OAuthClientMetadata(client_id="my-client-123", redirect_uris=["http://localhost/callback"])
provider.register_client(meta)
Defensive patterns

Strategy: type-guard

Validate before calling

if client_info.client_id is None:
    client_info = client_info.model_copy(update={"client_id": secrets.token_hex(16)})
await provider.register_client(client_info)

Type guard

def is_registerable(meta: OAuthClientMetadata) -> bool:
    return meta.client_id is not None and len(meta.client_id) > 0

Try / catch

try:
    await provider.register_client(client_info)
except ValueError as e:
    if "client_id is required" in str(e):
        client_info.client_id = secrets.token_hex(16)
        await provider.register_client(client_info)

Prevention

When it happens

Trigger: Calling register_client with OAuthClientMetadata whose client_id field is None — e.g. constructing metadata manually without assigning an id, or a registration handler forwarding pre-registration metadata.

Common situations: Hand-rolling dynamic client registration tests; generating client metadata client-side without assigning an explicit client_id; a custom registration endpoint that skips id generation before delegating to the provider.

Related errors


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