PrefectHQ/fastmcp · error · ClientNotFoundError
OAuth server rejected the static client credentials. Verify
Error message
OAuth server rejected the static client credentials. Verify that the client_id (and client_secret, if provided) are correct and that the client is registered with the server.
What it means
When the OAuth flow fails with ClientNotFoundError or ExpiredClientRegistrationError and the provider was configured with static client credentials (static_client_info), retrying cannot help — the credentials are fixed. FastMCP re-raises as ClientNotFoundError telling you to verify the client_id/client_secret and that the client is registered with the server.
Source
Thrown at fastmcp_slim/fastmcp/client/auth/oauth.py:475
"or use it with Client(auth=...) which provides the URL automatically."
)
try:
# First attempt with potentially cached credentials
async with aclosing(super().async_auth_flow(request)) as gen:
response = None
while True:
try:
# First iteration sends None, subsequent iterations send response
yielded_request = await gen.asend(response) # ty: ignore[invalid-argument-type]
response = yield yielded_request
except StopAsyncIteration:
break
except (ClientNotFoundError, ExpiredClientRegistrationError) as exc:
# Static credentials are fixed — retrying won't help. Surface the
# error so the user can correct their client_id / client_secret.
if self._static_client_info is not None:
raise ClientNotFoundError(
"OAuth server rejected the static client credentials. "
"Verify that the client_id (and client_secret, if provided) "
"are correct and that the client is registered with the server."
) from exc
logger.debug(
"OAuth client registration is invalid, clearing cache and retrying..."
)
# Clear cached state and retry once
self._initialized = False
await self.token_storage_adapter.clear()
# Retry with fresh registration
async with aclosing(super().async_auth_flow(request)) as gen:
response = None
while True:
try:
yielded_request = await gen.asend(response) # ty: ignore[invalid-argument-type]View on GitHub (pinned to 1f02114297)
Solutions
- Verify the client_id (and client_secret) values are correct for that exact server and correct any typos.
- Register the client with the OAuth server (or create it in the provider's admin console) if it does not exist.
- If the secret expired, rotate it and update the configuration.
- Alternatively drop the static credentials and let FastMCP perform dynamic client registration.
Example fix
// before: stale static credentials auth = OAuth(mcp_url=URL, static_client_id='old-client-id', static_client_secret='old-secret') // after: correct, currently registered credentials auth = OAuth(mcp_url=URL, static_client_id='current-client-id', static_client_secret='current-secret')
Defensive patterns
Strategy: validation
Validate before calling
# Confirm the static client exists before running the flow
token_resp = httpx.post(f'{issuer}/oauth/token', data={'client_id': CLIENT_ID, 'client_secret': CLIENT_SECRET, 'grant_type': 'client_credentials'})
if token_resp.status_code in (400, 401):
raise ValueError('Static OAuth client credentials are not registered/valid at this server') Try / catch
from fastmcp.client.auth import ClientNotFoundError
try:
async with client:
await client.list_tools()
except ClientNotFoundError as e:
if 'static client credentials' in str(e):
fix_or_register_static_client() # cannot self-heal: credentials are fixed
raise Prevention
- Keep static client_id/secret in per-environment config and verify them against the right server.
- Prefer dynamic client registration unless the provider forbids it, so credentials can self-heal.
- Track client secret expiry and rotate before it lapses.
- After a server wipe/redeploy, re-register static clients or switch to dynamic registration.
When it happens
Trigger: Using OAuth(..., static_client_id=... / client_metadata with fixed credentials) against a server that does not have that client registered, or whose registration was deleted/expired; the inner flow raised ClientNotFoundError or ExpiredClientRegistrationError, and the static-credentials branch in async_auth_flow converts it into this error.
Common situations: Typos in the configured client_id/secret; credentials copied from another environment; server database wiped so the statically referenced client no longer exists; client secret expired on the provider side.
Related errors
- OAuth client not found - cached credentials may be stale
- OAuth provider has no server URL. Either pass mcp_url to OAu
- MultiAuth requires at least a server or one verifier
- Assertion must include exp claim
- Assertion must include sub claim
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/346f92f058185994.
Report an issue: GitHub.