{"record":{"id":"865509907a05c9dc","repo":"PrefectHQ/fastmcp","slug":"could-not-extract-project-id-from-config-url-iss","errorCode":null,"errorMessage":"Could not extract project_id from config_url: {issuer_url}","messagePattern":"Could not extract project_id from config_url: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/auth/providers/descope.py","lineNumber":55,"sourceCode":"\n    issuer_url = openid_url[: -len(_OPENID_WK)]\n    parsed = urlparse(issuer_url)\n    parts = parsed.path.strip(\"/\").split(\"/\")\n    descope_base_url = f\"{parsed.scheme}://{parsed.netloc}\".rstrip(\"/\")\n\n    if \"agentic\" in parts:\n        index = parts.index(\"agentic\") + 1\n        project_id = parts[index] if index < len(parts) else \"\"\n    elif \"apps\" in parts:\n        index = parts.index(\"apps\") + 1\n        project_id = parts[index] if index < len(parts) else \"\"\n        if project_id == \"agentic\":\n            project_id = \"\"\n    else:\n        project_id = \"\"\n\n    if not project_id:\n        raise ValueError(f\"Could not extract project_id from config_url: {issuer_url}\")\n\n    return descope_base_url, project_id, issuer_url, openid_url\n\n\nasync def _discover_scopes(openid_configuration_url: str) -> list[str] | None:\n    try:\n        async with httpx2.AsyncClient() as client:\n            response = await client.get(openid_configuration_url, timeout=10.0)\n            response.raise_for_status()\n        scopes = response.json().get(\"scopes_supported\")\n        if isinstance(scopes, list):\n            parsed = [scope for scope in scopes if isinstance(scope, str)]\n            if not scopes or parsed:\n                return parsed\n    except Exception:\n        logger.warning(\n            \"Failed to fetch Descope OpenID configuration from %s\",\n            openid_configuration_url,","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/auth/providers/descope.py#L37-L73","documentation":"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.","triggerScenarios":"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 '').","commonSituations":"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.","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."],"exampleFix":"// before\nDescopeProvider(config_url=\"https://api.descope.com\")\n// after\nDescopeProvider(config_url=\"https://api.descope.com/v2/P2abcd1234\")","handlingStrategy":"validation","validationCode":"import re\ndef validate_descope_config_url(url: str) -> bool:\n    return re.search(r\"/P[0-9a-zA-Z]{8,}\", url) is not None","typeGuard":"def has_project_id(url: str) -> bool:\n    from urllib.parse import urlparse\n    parts = [p for p in urlparse(url).path.split(\"/\") if p]\n    return len(parts) > 1 and parts[-1] not in (\"\", \"agentic\")","tryCatchPattern":"try:\n    provider = DescopeProvider(config_url=url)\nexcept ValueError as e:\n    if \"Could not extract project_id\" in str(e):\n        provider = DescopeProvider(config_url=f\"https://api.descope.com/v2/{DESCOPE_PROJECT_ID}\")\n    else:\n        raise","preventionTips":["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"],"tags":["python","descope","oauth","configuration","url-parsing"],"backgroundTag":"invalid-issuer-url","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}