BerriAI/litellm · error · AzureOpenAIError
{req_token.text}
Error message
{req_token.text} What it means
LiteLLM posts the client-credentials request (client_assertion = your OIDC token) to {AZURE_AUTHORITY_HOST}/{tenant}/oauth2/v2.0/token. If Azure AD replies with any non-200 status, LiteLLM re-raises AzureOpenAIError with the same status code and the raw response body (message=req_token.text). The body is Azure AD's own error JSON, e.g. invalid_client, invalid_scope, or AADSTS70021.
Source
Thrown at litellm/llms/azure/common_utils.py:235
azure_ad_token_access_token = azure_ad_cache.get_cache(azure_ad_token_cache_key)
if azure_ad_token_access_token is not None:
return azure_ad_token_access_token
client: Final = litellm.module_level_client
req_token: Final = client.post(
f"{azure_authority_host}/{azure_tenant_id}/oauth2/v2.0/token",
data={
"client_id": azure_client_id,
"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,View on GitHub (pinned to 6c2dcb801b)
Solutions
- Read the AADSTS code in the message body — it names the exact cause (e.g. AADSTS70021: no federated identity credentials found for the service account).
- For AADSTS7000215/700213: fetch a fresh OIDC token immediately before the LiteLLM call; tokens are typically valid ~5-90 min.
- Verify AZURE_TENANT_ID matches the directory the app registration/federated identity lives in.
- For sovereign clouds set AZURE_AUTHORITY_HOST appropriately (e.g. https://login.microsoftonline.us) and keep scope under that cloud's resource.
- Confirm a federated identity credential exists linking your OIDC issuer/subject to the app registration (AZURE_CLIENT_ID).
Example fix
# before os.environ["AZURE_AUTHORITY_HOST"] = "https://login.microsoftonline.com" # default, wrong cloud # after (Azure US Government) os.environ["AZURE_AUTHORITY_HOST"] = "https://login.microsoftonline.us"
Defensive patterns
Strategy: retry
Try / catch
try:
resp = litellm.completion(..., azure_ad_token=oidc)
except AzureOpenAIError as e:
body = str(e)
if "AADSTS7000215" in body or "AADSTS700213" in body: # expired/invalid assertion
oidc = fetch_fresh_oidc()
resp = litellm.completion(..., azure_ad_token=oidc)
elif e.status_code in (429, 500, 503):
backoff_and_retry()
else:
raise Prevention
- Refresh OIDC assertions at ~50% of their TTL; don't reuse across long-lived processes.
- Log the AADSTS code from the message — it pinpoints tenant/client/scope issues.
- Keep AZURE_AUTHORITY_HOST consistent with your cloud and the scope's resource.
When it happens
Trigger: Expired or malformed OIDC assertion (AADSTS7000215 invalid_client); wrong tenant ID (AADSTS70001 application not found); scope not consented (e.g. requesting a scope other than https://cognitiveservices.azure.com/.default without permission); custom AZURE_AUTHORITY_HOST that is wrong or unreachable via proxy returns 404/403.
Common situations: Federated workload identity where the pod's service-account token TTL expired between fetch and use; copying a tenant ID from a different directory; sovereign-cloud deployments (AzureUSGovernment/AzureChina) that still point at login.microsoftonline.com; expired client secret when client_secret flow is mistakenly used.
Related errors
- Error from qdrant checking if /collections exist {collection
- OpenMeter logging error: {e.response.text}
- api_key is required for Azure AI Speech transcription.
- Failed to parse raw Azure embedding response: {json_error}
- AZURE_CLIENT_ID and AZURE_TENANT_ID must be set
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/2e50c69dd0c5663d.
Report an issue: GitHub.