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
- Assign a client_id to the OAuthClientMetadata before calling register_client (e.g. generate one with secrets.token_hex(16))
- If simulating RFC 7591 dynamic registration, let the server generate the id before storing rather than passing empty metadata
- 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
- Always mint a client_id when constructing OAuthClientMetadata by hand
- Centralize metadata construction in one factory so the id is never forgotten
- Assert client_id is set in tests that exercise registration
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
- client_id is required for client registration
- CIMD redirect_uri must have a host: {uri!r}
- CIMD document is not valid JSON: {e}
- CIMD client_id mismatch: document says '{doc.client_id}' but
- CIMD document must have jwks_uri or jwks for private_key_jwt
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/66cd3960354abc93.
Report an issue: GitHub.