{"record":{"id":"3b791d88ebf7ae38","repo":"BerriAI/litellm","slug":"azure-ad-token-expires-in-not-returned","errorCode":null,"errorMessage":"Azure AD Token expires_in not returned","messagePattern":"Azure AD Token expires_in not returned","errorType":"http","errorClass":"AzureOpenAIError","httpStatus":422,"severity":"error","filePath":"litellm/llms/azure/common_utils.py","lineNumber":248,"sourceCode":"            \"client_assertion\": oidc_token,\n        },\n    )\n\n    if req_token.status_code != 200:\n        raise AzureOpenAIError(\n            status_code=req_token.status_code,\n            message=req_token.text,\n        )\n\n    azure_ad_token_json: Final[_AzureAdTokenJson] = req_token.json()\n    azure_ad_token_access_token = azure_ad_token_json.get(\"access_token\", None)\n    azure_ad_token_expires_in: Final = azure_ad_token_json.get(\"expires_in\", None)\n\n    if azure_ad_token_access_token is None:\n        raise AzureOpenAIError(status_code=422, message=\"Azure AD Token access_token not returned\")\n\n    if azure_ad_token_expires_in is None:\n        raise AzureOpenAIError(status_code=422, message=\"Azure AD Token expires_in not returned\")\n\n    azure_ad_cache.set_cache(\n        key=azure_ad_token_cache_key,\n        value=azure_ad_token_access_token,\n        ttl=azure_ad_token_expires_in,\n    )\n\n    return azure_ad_token_access_token\n\n\ndef select_azure_base_url_or_endpoint(azure_client_params: dict):\n    azure_endpoint: Final = azure_client_params.get(\"azure_endpoint\", None)\n    if azure_endpoint is not None:\n        # see : https://github.com/openai/openai-python/blob/3d61ed42aba652b547029095a7eb269ad4e1e957/src/openai/lib/azure.py#L192\n        if \"/openai/deployments\" in azure_endpoint:\n            # this is base_url, not an azure_endpoint\n            azure_client_params[\"base_url\"] = azure_endpoint\n            azure_client_params.pop(\"azure_endpoint\")","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/llms/azure/common_utils.py#L230-L266","documentation":"The token endpoint returned 200 with an access_token but no expires_in field. LiteLLM needs expires_in as the TTL for the in-process azure_ad_cache entry, so it raises 422 rather than guessing a lifetime. Like 1023, this indicates a nonstandard token endpoint or intermediary answered.","triggerScenarios":"A custom token broker or mock returning {access_token: ...} without expiry; an API gateway rewriting the response body; OAuth-compliant servers that (rarely) omit expires_in when a default is assumed.","commonSituations":"Internal token-broker services standing in for Azure AD; contract tests with hand-written fixtures; older ADAL endpoints returning non-v2 payloads.","solutions":["Ensure the endpoint you hit is the real v2.0 endpoint: {authority}/{tenant}/oauth2/v2.0/token always returns expires_in.","If you operate a broker/mock in the middle, include expires_in (seconds) in its JSON response.","Check that no proxy is mangling the response body (compare curl output with the app's view)."],"exampleFix":"# before\n{ \"access_token\": \"eyJ...\" }\n\n# after\n{ \"access_token\": \"eyJ...\", \"expires_in\": 3599 }","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    resp = litellm.completion(..., azure_ad_token=oidc)\nexcept AzureOpenAIError as e:\n    if e.status_code == 422 and \"expires_in\" in str(e):\n        log.error(\"nonstandard token endpoint: response lacked expires_in\")\n    raise","preventionTips":["Only use real Azure authorities for token exchange in production.","If you run a token broker, contract-test that it returns access_token, expires_in, token_type.","Avoid adal v1 endpoints; use the v2.0 endpoint LiteLLM targets."],"tags":["azure","oauth2","cache","protocol"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}