PrefectHQ/fastmcp · error · ValueError
Could not extract project_id from config_url: {issuer_url}
Error message
Could not extract project_id from config_url: {issuer_url} What it means
DescopeProvider._parse_descope_config_url derives the Descope project_id from the config_url (issuer URL) — typically a path segment like /v2/<project-id> or a 'ProjectId' path part. If parsing yields no project_id (including the special 'agentic' placeholder case), the provider cannot build Descope endpoints, so it raises ValueError.
Source
Thrown at fastmcp_slim/fastmcp/server/auth/providers/descope.py:55
issuer_url = openid_url[: -len(_OPENID_WK)]
parsed = urlparse(issuer_url)
parts = parsed.path.strip("/").split("/")
descope_base_url = f"{parsed.scheme}://{parsed.netloc}".rstrip("/")
if "agentic" in parts:
index = parts.index("agentic") + 1
project_id = parts[index] if index < len(parts) else ""
elif "apps" in parts:
index = parts.index("apps") + 1
project_id = parts[index] if index < len(parts) else ""
if project_id == "agentic":
project_id = ""
else:
project_id = ""
if not project_id:
raise ValueError(f"Could not extract project_id from config_url: {issuer_url}")
return descope_base_url, project_id, issuer_url, openid_url
async def _discover_scopes(openid_configuration_url: str) -> list[str] | None:
try:
async with httpx2.AsyncClient() as client:
response = await client.get(openid_configuration_url, timeout=10.0)
response.raise_for_status()
scopes = response.json().get("scopes_supported")
if isinstance(scopes, list):
parsed = [scope for scope in scopes if isinstance(scope, str)]
if not scopes or parsed:
return parsed
except Exception:
logger.warning(
"Failed to fetch Descope OpenID configuration from %s",
openid_configuration_url,View on GitHub (pinned to 1f02114297)
Solutions
- Include the project id in config_url, e.g. https://api.descope.com/v2/P2abcd1234 or https://api.descope.com/<project_id>.
- Copy the Project ID from the Descope console (Project Settings) and rebuild the URL.
- If using an agentic URL form, supply the project id through the provider argument that DescopeProvider supports instead of relying on config_url parsing.
Example fix
// before DescopeProvider(config_url="https://api.descope.com") // after DescopeProvider(config_url="https://api.descope.com/v2/P2abcd1234")
Defensive patterns
Strategy: validation
Validate before calling
import re
def validate_descope_config_url(url: str) -> bool:
return re.search(r"/P[0-9a-zA-Z]{8,}", url) is not None Type guard
def has_project_id(url: str) -> bool:
from urllib.parse import urlparse
parts = [p for p in urlparse(url).path.split("/") if p]
return len(parts) > 1 and parts[-1] not in ("", "agentic") Try / catch
try:
provider = DescopeProvider(config_url=url)
except ValueError as e:
if "Could not extract project_id" in str(e):
provider = DescopeProvider(config_url=f"https://api.descope.com/v2/{DESCOPE_PROJECT_ID}")
else:
raise Prevention
- Store the Descope Project ID separately and build config_url from it
- Validate the URL shape (must contain the project id path segment) in config loading
- Never copy a bare api.descope.com base URL as the config_url
When it happens
Trigger: DescopeProvider(config_url='https://api.descope.com') or any URL whose path doesn't contain the project id, so project_id parses to '' (or 'agentic' which is reset to '').
Common situations: Omitting the project id from the issuer URL; copying a bare Descope API base URL; using the 'agentic' URL form which intentionally maps to empty project_id and requires an explicit project id elsewhere; typos like 'v1' paths without the id.
Related errors
- Cannot specify 'required_scopes' when providing a custom tok
- Missing required OIDC endpoints
- AzureProvider requires at least one non-OIDC scope in requir
- OAuth provider has no server URL. Either pass mcp_url to OAu
- OAuth server rejected the static client credentials. Verify
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/865509907a05c9dc.
Report an issue: GitHub.