crewAIInc/crewAI · critical · HTTPException

OIDC not initialized

Error message

OIDC not initialized

What it means

Raised by OIDCAuth.authenticate() when the private _jwk_client attribute is None, meaning the scheme was never initialized with a JWKS endpoint. It returns HTTP 500 because it is an internal state error: the scheme object exists but cannot perform validation. Under normal construction the model validator builds the PyJWKClient from jwks_url, so reaching this branch implies the validator was skipped or the object was mutated.

Source

Thrown at lib/crewai/src/crewai/a2a/auth/server_schemes.py:275

            else f"{str(self.issuer).rstrip('/')}/.well-known/jwks.json"
        )
        self._jwk_client = PyJWKClient(jwks_url, lifespan=self.jwks_cache_ttl)
        return self

    async def authenticate(self, token: str) -> AuthenticatedUser:
        """Authenticate using OIDC JWT validation.

        Args:
            token: The JWT to authenticate.

        Returns:
            AuthenticatedUser on successful authentication.

        Raises:
            HTTPException: If authentication fails.
        """
        if self._jwk_client is None:
            raise HTTPException(
                status_code=HTTP_500_INTERNAL_SERVER_ERROR,
                detail="OIDC not initialized",
            )

        try:
            signing_key = self._jwk_client.get_signing_key_from_jwt(token)

            claims = jwt.decode(
                token,
                signing_key.key,
                algorithms=self.algorithms,
                audience=self.audience,
                issuer=str(self.issuer).rstrip("/"),
                leeway=self.clock_skew_seconds,
                options={
                    "require": self.required_claims,
                },
            )

View on GitHub (pinned to 754d7323be)

Solutions

  1. Construct OIDCAuth with a valid jwks_url so the model validator creates the PyJWKClient: OIDCAuth(jwks_url='https://idp/.well-known/jwks.json', ...).
  2. Never instantiate the scheme with model_construct(); let pydantic run validators.
  3. Add a startup assertion that getattr(scheme, '_jwk_client', None) is not None before serving traffic.
  4. If configuring from discovery documents, fail startup when the discovered jwks_uri is missing.

Example fix

# before
scheme = OIDCAuth.model_construct(issuer="https://idp")  # validator skipped, _jwk_client=None

# after
scheme = OIDCAuth(
    issuer="https://idp",
    jwks_url="https://idp/.well-known/jwks.json",
    audience="my-audience",
)
Defensive patterns

Strategy: validation

Validate before calling

from crewai.a2a.auth.server_schemes import OIDCAuth

scheme = OIDCAuth(
    issuer="https://idp", jwks_url="https://idp/.well-known/jwks.json", audience="api"
)
# smoke check before serving traffic
assert scheme._jwk_client is not None, "OIDCAuth initialized without a JWKS client"

Type guard

def is_initialized_oidc(scheme: ServerAuthScheme) -> bool:
    """True when the scheme is an OIDCAuth with a live JWKS client."""
    return (
        type(scheme).__name__ == "OIDCAuth"
        and getattr(scheme, "_jwk_client", None) is not None
    )

Prevention

When it happens

Trigger: Calling authenticate() on an OIDCAuth instance constructed without a usable jwks_url (or after manually clearing _jwk_client); bypassing pydantic validation via model_construct(); or an OIDC provider that exposes no JWKS URL so initialization silently left the client as None.

Common situations: Building the scheme from a partial dict that omitted jwks_url; using model_construct() or object reuse/copying that skips model_validator(mode='after'); a discovery step that failed upstream and left a half-initialized scheme.

Related errors


AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15). Data as JSON: /api/errors/9ab56173a4a823a2. Report an issue: GitHub.