PrefectHQ/fastmcp · error · ValueError
Either config_url (new API) or both project_id and descope_b
Error message
Either config_url (new API) or both project_id and descope_base_url (old API) must be provided
What it means
DescopeAuthProvider requires either the new API form (config_url) or the old API form (project_id plus descope_base_url). The constructor raises this ValueError when neither form is satisfied — typically when only project_id is given without a base URL, or a base URL without project_id, or neither argument at all.
Source
Thrown at fastmcp_slim/fastmcp/server/auth/providers/descope.py:171
)
if config_url is not None:
(
self.descope_base_url,
self.project_id,
issuer_url,
self.openid_configuration_url,
) = _parse_descope_config_url(str(config_url))
elif project_id is not None and descope_base_url is not None:
self.project_id = project_id
descope_base_url_str = str(descope_base_url).rstrip("/")
if not descope_base_url_str.startswith(("http://", "https://")):
descope_base_url_str = f"https://{descope_base_url_str}"
self.descope_base_url = descope_base_url_str
issuer_url = f"{self.descope_base_url}/v1/apps/{self.project_id}"
self.openid_configuration_url = f"{issuer_url}{_OPENID_WK}"
else:
raise ValueError(
"Either config_url (new API) or both project_id and descope_base_url (old API) must be provided"
)
self.oauth_authorization_server_metadata_url = (
self.openid_configuration_url.replace(_OPENID_WK, _OAUTH_WK)
)
# Advertised scopes are discovered from Descope's OpenID configuration
# only when the caller supplied neither explicit advertised scopes nor
# required scopes. Discovery is deferred to the first protected resource
# metadata request (see get_routes) so construction never performs I/O
# and a transient failure can be retried instead of being frozen for the
# provider's lifetime.
custom_verifier_scopes = (
token_verifier.scopes_supported if token_verifier is not None else []
)
self._scopes_discovery_enabled = (
parsed_scopes_supported is NoneView on GitHub (pinned to 1f02114297)
Solutions
- Pass config_url pointing to Descope's OIDC configuration (new API), e.g. config_url=f"https://api.descope.com/v1/apps/<project_id>/.well-known/openid-configuration"
- Alternatively pass BOTH project_id and descope_base_url (e.g. project_id='P2abc...', descope_base_url='https://api.descope.com')
- Verify keyword argument names and that the values are non-empty strings, not None from failed env lookups
Example fix
// before
auth = DescopeAuthProvider(project_id=os.getenv("DESCOPE_PROJECT_ID"))
// after
auth = DescopeAuthProvider(
project_id=os.environ["DESCOPE_PROJECT_ID"],
descope_base_url="https://api.descope.com",
)
# or the new API:
auth = DescopeAuthProvider(
config_url="https://api.descope.com/v1/apps/P2abc/.well-known/openid-configuration"
) Defensive patterns
Strategy: validation
Validate before calling
config_url = os.getenv("DESCOPE_CONFIG_URL")
project_id = os.getenv("DESCOPE_PROJECT_ID")
base_url = os.getenv("DESCOPE_BASE_URL")
if not config_url and not (project_id and base_url):
raise ValueError("Provide either DESCOPE_CONFIG_URL or both DESCOPE_PROJECT_ID and DESCOPE_BASE_URL")
provider = DescopeAuthProvider(config_url=config_url) if config_url else DescopeAuthProvider(project_id=project_id, descope_base_url=base_url) Type guard
def has_descope_args(config_url: str | None, project_id: str | None, base_url: str | None) -> bool:
return bool(config_url) or bool(project_id and base_url) Try / catch
try:
provider = DescopeAuthProvider(project_id=pid, descope_base_url=base)
except ValueError as e:
logger.error("Descope auth misconfigured: %s", e)
raise SystemExit(1) from e Prevention
- Validate provider configuration at application startup, not lazily
- Prefer the single config_url API to reduce the argument combinations
- Use required env lookups (os.environ[...]) instead of os.getenv with None fallbacks
When it happens
Trigger: Calling DescopeAuthProvider(...) with: (1) no arguments, (2) only project_id but no descope_base_url, (3) only descope_base_url but no project_id, (4) both but one is None/empty so the `if` branch fails.
Common situations: Migrating from the old Descope setup to the new config_url-based setup and removing old kwargs piecemeal; copying example code that assumes env vars supply project_id/base_url; typos in keyword names (e.g. base_url instead of descope_base_url) silently leaving them unset.
Understand the failure class
Background: "X is required", "must be set", "cannot be empty": the missing-required-config error family, from Vertex AI project/location to WeChat keys — this error's family across 18 libraries.
Related errors
- OAuth provider has no server URL. Either pass mcp_url to OAu
- OAuth server rejected the static client credentials. Verify
- jwt_signing_key is required when upstream_client_secret is n
- Unsupported token_endpoint_auth_method: {method!r}. Supporte
- Cannot specify 'required_scopes' when providing a custom tok
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/101f7f29519ddb42.
Report an issue: GitHub.