BerriAI/litellm · error · AzureOpenAIError
Azure AD Token access_token not returned
Error message
Azure AD Token access_token not returned
What it means
The token endpoint returned HTTP 200 but the JSON body had no access_token field. LiteLLM treats this as a 422 protocol violation because a successful client-credentials response must contain access_token per the OAuth 2.0 spec. It almost always means a non-Azure endpoint or a proxy answered 200 with a different payload.
Source
Thrown at litellm/llms/azure/common_utils.py:245
"grant_type": "client_credentials",
"scope": scope,
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": oidc_token,
},
)
if req_token.status_code != 200:
raise AzureOpenAIError(
status_code=req_token.status_code,
message=req_token.text,
)
azure_ad_token_json: Final[_AzureAdTokenJson] = req_token.json()
azure_ad_token_access_token = azure_ad_token_json.get("access_token", None)
azure_ad_token_expires_in: Final = azure_ad_token_json.get("expires_in", None)
if azure_ad_token_access_token is None:
raise AzureOpenAIError(status_code=422, message="Azure AD Token access_token not returned")
if azure_ad_token_expires_in is None:
raise AzureOpenAIError(status_code=422, message="Azure AD Token expires_in not returned")
azure_ad_cache.set_cache(
key=azure_ad_token_cache_key,
value=azure_ad_token_access_token,
ttl=azure_ad_token_expires_in,
)
return azure_ad_token_access_token
def select_azure_base_url_or_endpoint(azure_client_params: dict):
azure_endpoint: Final = azure_client_params.get("azure_endpoint", None)
if azure_endpoint is not None:
# see : https://github.com/openai/openai-python/blob/3d61ed42aba652b547029095a7eb269ad4e1e957/src/openai/lib/azure.py#L192
if "/openai/deployments" in azure_endpoint:View on GitHub (pinned to 6c2dcb801b)
Solutions
- Print/inspect the actual response your environment gets: curl -sv $AZURE_AUTHORITY_HOST/$AZURE_TENANT_ID/oauth2/v2.0/token to see what is answering.
- Fix AZURE_AUTHORITY_HOST to the real authority (default https://login.microsoftonline.com or the sovereign equivalent).
- Bypass corporate proxies/SSL inspection for login.microsoftonline.com (add to NO_PROXY or proxy allowlist).
- In tests, make the mocked endpoint return both access_token and expires_in.
Example fix
# before (mock returns incomplete body)
{ "token_type": "Bearer", "expires_in": 3599 }
# after
{ "token_type": "Bearer", "expires_in": 3599, "access_token": "eyJ..." } Defensive patterns
Strategy: validation
Validate before calling
import os
def sane_authority() -> None:
host = os.getenv("AZURE_AUTHORITY_HOST", "https://login.microsoftonline.com")
if "login.microsoftonline" not in host and "login.microsoftonline.us" not in host and "login.chinacloudapi" not in host:
raise ConfigError(f"Suspicious AZURE_AUTHORITY_HOST: {host}") Try / catch
try:
resp = litellm.completion(..., azure_ad_token=oidc)
except AzureOpenAIError as e:
if e.status_code == 422 and "access_token not returned" in str(e):
log.error("token endpoint answered 200 without access_token — check AZURE_AUTHORITY_HOST/proxy")
raise Prevention
- curl the authority endpoint once in deployment smoke tests and assert access_token in the JSON.
- Do not put token endpoints behind rewriting proxies.
- Mock servers must return spec-compliant token responses in tests.
When it happens
Trigger: AZURE_AUTHORITY_HOST pointing at a service that returns 200 with HTML/JSON lacking access_token (a corporate proxy's captive portal, a misconfigured gateway); an API-management wrapper in front of the authority; a mocked test server returning an incomplete fixture.
Common situations: SSL-inspecting proxies rewriting the token response; typos in AZURE_AUTHORITY_HOST that hit a benign web server; test fixtures with only expires_in; adal/MSAL middleware in the path.
Related errors
- Azure AD Token expires_in not returned
- {req_token.text}
- Azure api base not found
- Azure Document Intelligence returned 202 but no Operation-Lo
- Azure Document Intelligence: rejected polling URL ({ssrf_err
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/23475fc5e4c7034e.
Report an issue: GitHub.