{"record":{"id":"8440d20f3bd49bb5","repo":"crewAIInc/crewAI","slug":"oauth2-introspection-not-configured","errorCode":null,"errorMessage":"OAuth2 introspection not configured","messagePattern":"OAuth2 introspection not configured","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"critical","filePath":"lib/crewai/src/crewai/a2a/auth/server_schemes.py","lineNumber":582,"sourceCode":"            raise HTTPException(\n                status_code=HTTP_503_SERVICE_UNAVAILABLE,\n                detail=\"Unable to fetch signing keys\",\n            ) from None\n        except jwt.InvalidTokenError as e:\n            logger.debug(\n                \"OAuth2 authentication failed\",\n                extra={\"reason\": \"invalid_token\", \"error\": str(e), \"scheme\": \"oauth2\"},\n            )\n            raise HTTPException(\n                status_code=HTTP_401_UNAUTHORIZED,\n                detail=\"Invalid or missing authentication credentials\",\n            ) from None\n\n    async def _authenticate_introspection(self, token: str) -> AuthenticatedUser:\n        \"\"\"Authenticate using OAuth2 token introspection (RFC 7662).\"\"\"\n\n        if not self.introspection_url:\n            raise HTTPException(\n                status_code=HTTP_500_INTERNAL_SERVER_ERROR,\n                detail=\"OAuth2 introspection not configured\",\n            )\n\n        try:\n            async with httpx.AsyncClient() as client:\n                response = await client.post(\n                    str(self.introspection_url),\n                    data={\"token\": token},\n                    auth=(\n                        self.introspection_client_id or \"\",\n                        self.introspection_client_secret.get_secret_value()\n                        if self.introspection_client_secret\n                        else \"\",\n                    ),\n                )\n                response.raise_for_status()\n                introspection_result = response.json()","sourceCodeStart":564,"sourceCodeEnd":600,"githubUrl":"https://github.com/crewAIInc/crewAI/blob/754d7323beb2fd042e33444a115ea2d5a47193f0/lib/crewai/src/crewai/a2a/auth/server_schemes.py#L564-L600","documentation":"Raised by OAuth2ServerAuth._authenticate_introspection() when it is invoked but introspection_url is empty. It maps to HTTP 500: the internal state (routing to introspection without an introspection endpoint) contradicts the model validator's guarantee, so in practice it indicates an object that bypassed normal construction or was mutated after validation.","triggerScenarios":"authenticate() dispatches to _authenticate_introspection() because _jwk_client is None, yet introspection_url was also cleared — only reachable via model_construct() or manual attribute surgery, since a validly constructed scheme has at least one endpoint.","commonSituations":"Tests that build schemes with model_construct(); code that reassigns config fields post-construction; framework code deserializing schemes without running validators.","solutions":["Construct OAuth2ServerAuth normally with introspection_url so validation guarantees consistency.","Avoid model_construct() and in-place mutation of auth scheme objects; rebuild them instead.","Add a startup sanity check: the scheme must have _jwk_client or a non-empty introspection_url.","If deserializing from config, round-trip through OAuth2ServerAuth.model_validate()."],"exampleFix":"# before\nauth = OAuth2ServerAuth.model_construct()  # no endpoints; introspection path -> 500\n\n# after\nauth = OAuth2ServerAuth(\n    introspection_url=\"https://idp/oauth/introspect\",\n    introspection_client_id=\"svc\",\n    introspection_client_secret=\"***\",\n)","handlingStrategy":"validation","validationCode":"from crewai.a2a.auth.server_schemes import OAuth2ServerAuth\n\nauth = OAuth2ServerAuth(\n    introspection_url=\"https://idp/oauth/introspect\",\n    introspection_client_id=\"svc\",\n    introspection_client_secret=\"***\",\n)\nassert auth.introspection_url, \"introspection must be configured when used\"","typeGuard":"def is_introspection_ready(scheme) -> bool:\n    \"\"\"True when the scheme can call an introspection endpoint.\"\"\"\n    return bool(getattr(scheme, \"introspection_url\", None))","tryCatchPattern":null,"preventionTips":["Build schemes with model_validate/__init__ only, never model_construct().","Do not mutate auth scheme attributes after construction.","Prefer raising a startup error over letting an inconsistent scheme serve 500s."],"tags":["a2a","oauth2","introspection","initialization","http-500"],"backgroundTag":null,"analyzedSha":"754d7323beb2fd042e33444a115ea2d5a47193f0","analyzedAt":"2026-08-15T04:06:56.746Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}