{"record":{"id":"60b82e0c3b4f8a08","repo":"PrefectHQ/fastmcp","slug":"cimd-redirect-uri-must-have-a-scheme-e-g-http","errorCode":null,"errorMessage":"CIMD redirect_uri must have a scheme (e.g. http:// or https://): {uri!r}","messagePattern":"CIMD redirect_uri must have a scheme \\(e\\.g\\. http:// or https://\\): (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/auth/cimd.py","lineNumber":158,"sourceCode":"        if v in forbidden:\n            raise ValueError(\n                f\"CIMD documents cannot use shared-secret auth methods: {v}. \"\n                \"Use 'none' or 'private_key_jwt' instead.\"\n            )\n        return v\n\n    @field_validator(\"redirect_uris\")\n    @classmethod\n    def validate_redirect_uris(cls, v: list[str]) -> list[str]:\n        \"\"\"Ensure redirect_uris is non-empty and each entry is a valid URI.\"\"\"\n        if not v:\n            raise ValueError(\"CIMD documents must include at least one redirect_uri\")\n        for uri in v:\n            if not uri or not uri.strip():\n                raise ValueError(\"CIMD redirect_uris must be non-empty strings\")\n            parsed = urlparse(uri)\n            if not parsed.scheme:\n                raise ValueError(\n                    f\"CIMD redirect_uri must have a scheme (e.g. http:// or https://): {uri!r}\"\n                )\n            if not parsed.netloc and not uri.startswith(\"urn:\"):\n                raise ValueError(f\"CIMD redirect_uri must have a host: {uri!r}\")\n        return v\n\n\nclass CIMDValidationError(Exception):\n    \"\"\"Raised when CIMD document validation fails.\"\"\"\n\n\nclass CIMDFetchError(Exception):\n    \"\"\"Raised when CIMD document fetching fails.\"\"\"\n\n\n@dataclass\nclass _CIMDCacheEntry:\n    \"\"\"Cached CIMD document and associated HTTP cache metadata.\"\"\"","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/auth/cimd.py#L140-L176","documentation":"A Pydantic ValueError from the redirect_uris validator (fastmcp_slim/fastmcp/server/auth/cimd.py:148) raised when a redirect_uri entry is a non-empty string but urlparse finds no scheme. Redirect URIs must be absolute URLs (http:// or https://, or urn: URIs with a host); bare paths like '/callback' or schemeless hosts like 'client.example.com/cb' are rejected.","triggerScenarios":"A CIMD document lists a relative path ('/oauth/callback'), a schemeless host ('client.example.com/cb'), or a mistyped scheme (e.g. 'https:/example.com/cb' with a single slash) in redirect_uris, so urlparse(uri).scheme comes back empty.","commonSituations":"Authoring the document with values copied from web-framework route paths instead of full URLs; assuming relative URIs will be resolved against the document URL (they will not); typos in the '://' scheme separator.","solutions":["Use fully-qualified absolute URIs including the scheme, e.g. 'https://client.example.com/callback'.","For native apps, register a proper custom-scheme or loopback URI (e.g. 'http://127.0.0.1:PORT/cb') rather than a bare path.","Validate each URI with urllib.parse.urlparse locally before publishing: assert scheme and netloc are non-empty.","Check for '://' typos in scheme syntax."],"exampleFix":"// before\n\"redirect_uris\": [\"/oauth/callback\"]\n// after\n\"redirect_uris\": [\"https://client.example.com/oauth/callback\"]","handlingStrategy":"validation","validationCode":"from urllib.parse import urlparse\nfor uri in doc.get('redirect_uris', []):\n    p = urlparse(uri)\n    if not p.scheme or not (p.netloc or uri.startswith('urn:')):\n        raise ValueError(f'redirect_uri must be absolute with scheme: {uri!r}')","typeGuard":"def is_absolute_uri(uri: str) -> bool:\n    p = urlparse(uri)\n    return bool(p.scheme) and (bool(p.netloc) or uri.startswith('urn:'))","tryCatchPattern":"from pydantic import ValidationError\ntry:\n    document = CIMDDocument.model_validate(raw_doc)\nexcept ValidationError as e:\n    logger.error('redirect_uri scheme error: %s', e)\n    raise HTTPException(400, 'invalid_client_metadata') from e","preventionTips":["Store full absolute URLs (https://host/path) in config, never framework route paths.","Use http://127.0.0.1:PORT for loopback or a registered custom scheme for native apps.","Validate each URI with urlparse before publishing the CIMD document.","Watch for '://' typos (single slash) that silently drop the scheme."],"tags":["pydantic","cimd","validation","redirect-uri","url-parsing"],"backgroundTag":"schema-validation-failed","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}