PrefectHQ/fastmcp · error · AuthenticationError

Invalid client_id

Error message

Invalid client_id

What it means

During OAuth client authentication at the token endpoint, the provider looks up the submitted client_id via provider.get_client(); if no registered client matches, an AuthenticationError('Invalid client_id') is raised. This only happens when client_id is present in the form body; client_secret_basic (credentials in the Authorization header) is delegated to the SDK instead.

Source

Thrown at fastmcp_slim/fastmcp/server/auth/auth.py:268

        self, request: Request
    ) -> OAuthClientInformationFull:
        """Authenticate a client from an HTTP request.

        Extends SDK authentication to support private_key_jwt for CIMD clients.
        Delegates to SDK for client_secret_basic (Authorization header) and
        client_secret_post (form body) authentication.
        """
        form_data = await request.form()
        client_id = form_data.get("client_id")

        # If client_id is not in form data, delegate to SDK
        # This handles client_secret_basic which sends credentials in Authorization header
        if not client_id:
            return await super().authenticate_request(request)

        client = await self.provider.get_client(str(client_id))
        if not client:
            raise AuthenticationError("Invalid client_id")

        # Handle private_key_jwt authentication for CIMD clients
        if client.token_endpoint_auth_method == "private_key_jwt":
            # Validate assertion parameters
            assertion_type = form_data.get("client_assertion_type")
            assertion = form_data.get("client_assertion")

            if assertion_type != JWT_BEARER_ASSERTION_TYPE:
                raise AuthenticationError(
                    f"Invalid client_assertion_type: expected {JWT_BEARER_ASSERTION_TYPE}"
                )

            if not assertion or not isinstance(assertion, str):
                raise AuthenticationError("Missing client_assertion")

            # Validate the JWT assertion using CIMD manager
            try:
                await self._cimd_manager.validate_private_key_jwt(

View on GitHub (pinned to 1f02114297)

Solutions

  1. Verify the client_id sent by the OAuth client exactly matches a registered client in your provider's store.
  2. Re-register the client (or fix the client's configuration) so get_client() returns its information.
  3. Confirm you're hitting the right environment — registration data differs between dev/staging/prod.
  4. For CIMD clients, ensure the client_id metadata URL is reachable and valid, or fall back to a registered client_id.

Example fix

// before
# client config
client_id = "my-app"  # not registered on this server

// after
# register first, then use the issued id
client_id = "registered-client-id-from-provider"
Defensive patterns

Strategy: validation

Validate before calling

async def client_id_is_registered(provider, client_id: str) -> bool:
    return await provider.get_client(client_id) is not None

Try / catch

# server side (returned to client as OAuth error)
try:
    client = await provider.get_client(client_id)
except AuthenticationError as e:
    return JSONResponse(status_code=401, content={'error': 'invalid_client'})

Prevention

When it happens

Trigger: POST to the token endpoint with client_id in form data whose value is not registered with the OAuth provider (auth.py:266-268) — e.g. client was deleted, wrong environment, or a CIMD/private_key_jwt client whose metadata URL isn't fetchable/registered.

Common situations: Client credentials from dev/staging used against a production server; OAuth client registration never completed or was rotated; CIMD client_id (an HTTPS URL) unreachable or mistyped; clients configured with client_secret_basic still putting client_id in the body with a mismatched secret.

Related errors


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