Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
This guide covers common issues specific to confidential client applications (web apps, web APIs, and daemon/service apps). For general exception handling, see Handle errors and exceptions in MSAL.NET.
Important
The AADSTS error codes in this article are references for humans who are diagnosing a problem. Don't extract them from error messages and branch on them programmatically — they aren't a stable API and can change. For dynamic handling, branch on the MSAL exception type instead. For example, catch MsalUiRequiredException to reprompt the user, and check MsalServiceException.Claims to send a claims challenge back to the client.
Throttling (HTTP 429 and AADSTS50196)
Symptoms
MsalServiceExceptionwith HTTP status code 429- Error code
AADSTS50196— "The server terminated an operation because it encountered a loop" MsalThrottledServiceException(MSAL 4.47.0+)
Common causes
- Missing or misconfigured token cache — Every call goes to Microsoft Entra ID instead of serving tokens from the cache.
- Requesting tokens in a tight loop — For example, calling
AcquireTokenForClienton every incoming request without checking the cache first. - Too many distinct scopes/resources — Each unique scope produces a separate cached token.
Resolution
Always call
AcquireTokenSilentfirst (for delegated flows) or ensure the token cache is configured (for client credentials). MSAL's built-in cache handles deduplication automatically when properly configured.Verify your token cache is working. Check
AuthenticationResult.AuthenticationResultMetadata.TokenSource— if it always showsIdentityProviderinstead ofCache, your cache isn't being used.var result = await app.AcquireTokenForClient(scopes).ExecuteAsync(); if (result.AuthenticationResultMetadata.TokenSource == TokenSource.IdentityProvider) { // This should NOT happen on every call - investigate cache configuration logger.LogWarning("Token was fetched from IdP, not cache. CacheRefreshReason: {Reason}", result.AuthenticationResultMetadata.CacheRefreshReason); }Respect
Retry-Afterheaders. When you receive a 429, theMsalServiceException.Headersproperty includes aRetryAftervalue. It can be expressed either as a delta or as an absolute date, so handle both and never pass a negative delay toTask.Delay:catch (MsalServiceException ex) when (ex.StatusCode == 429) { TimeSpan delay = TimeSpan.FromSeconds(60); var retryAfter = ex.Headers?.RetryAfter; if (retryAfter?.Delta is TimeSpan delta) { delay = delta; } else if (retryAfter?.Date is DateTimeOffset date) { delay = date - DateTimeOffset.UtcNow; } // Clock skew between the server and the client can produce a negative value. if (delay < TimeSpan.Zero) { delay = TimeSpan.Zero; } await Task.Delay(delay); }Use a single
ConfidentialClientApplicationinstance per session (not per request). See High availability for guidance.
Tip
Starting with MSAL 4.47.0, throttled responses throw MsalThrottledServiceException (a subclass of MsalServiceException) which makes it easier to distinguish throttling from other service errors.
Network instability and socket exceptions
Symptoms
HttpRequestException(often with an innerSocketException) when acquiring tokens.- Intermittent failures during calls to the Microsoft Entra token endpoint.
- High latency in MSAL operations (
AuthenticationResult.AuthenticationResultMetadata.DurationTotalInMs).
Common causes
- Not caching tokens — Without caching, every token request results in a network call to Microsoft Entra ID. This increases exposure to transient network failures and socket exhaustion.
- Service outage or local network issues — The token endpoint may be temporarily unavailable, or the local network is unstable.
- Custom
HttpClientoverriding MSAL's default — MSAL's built-inHttpClientis designed to be scalable. If you override it, connection management becomes your responsibility. - Firewall or network rules — Recent updates to network or firewall rules may be blocking outbound traffic to
login.microsoftonline.com.
Solution
Enable and verify token caching. Caching reduces the number of outbound network calls, shielding your app from transient network failures.
To verify that tokens are being served from cache, check the TokenSource property on the authentication result:
var result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
if (result.AuthenticationResultMetadata.TokenSource == TokenSource.IdentityProvider)
{
// Token was fetched from the network - this should only happen on the first call or after expiry
logger.LogWarning("Token not served from cache. CacheRefreshReason: {Reason}",
result.AuthenticationResultMetadata.CacheRefreshReason);
}
If TokenSource consistently returns IdentityProvider instead of Cache, your token cache is not configured correctly.
For web apps and web APIs, use a distributed token cache (for example, Redis). See High availability for configuration guidance.
If you provide a custom HttpClient, ensure it's long-lived and properly manages connection pooling. See Providing your own HttpClient.
Verify if there are any recent updates to network or firewall rules that might have caused connectivity issues to login.microsoftonline.com and regional endpoints.
On-Behalf-Of (OBO) failures
MsalUiRequiredException — conditional access, MFA, or incremental consent
Symptoms
AcquireTokenOnBehalfOf throws MsalUiRequiredException, often with interaction_required and with the Claims property set. This typically happens when the downstream API (for example, Microsoft Graph) is protected by a conditional access policy — such as an MFA requirement — that the web API itself doesn't enforce.
Common causes
- A conditional access policy on the downstream resource requires a user interaction the incoming token doesn't satisfy (MFA, compliant device, sign-in frequency).
- The user hasn't consented to the downstream scopes yet (incremental consent).
Resolution
Your web API can't satisfy the policy on its own — only the client that owns the user interaction can. The API must return the claims challenge to the client, and the client must re-acquire a token that satisfies the policy.
Catch
MsalUiRequiredExceptionand returnHTTP 401with aWWW-Authenticateheader carrying the claims fromex.Claims:catch (MsalUiRequiredException ex) when (ex.Claims != null) { // ASP.NET Core web API httpResponse.StatusCode = (int)HttpStatusCode.Unauthorized; // HTTP 401 httpResponse.Headers[HeaderNames.WWWAuthenticate] = $"Bearer realm=\"\", authorization_uri=\"https://login.microsoftonline.com/common/oauth2/authorize\", error=\"insufficient_claims\", claims=\"{Convert.ToBase64String(Encoding.UTF8.GetBytes(ex.Claims))}\""; }In the client, parse the header and pass the claims back into the next token request:
WwwAuthenticateParameters wwwParams = WwwAuthenticateParameters.CreateFromAuthenticationHeaders(response.Headers, "Bearer"); // Desktop or mobile app await app.AcquireTokenInteractive(scopes) .WithClaims(wwwParams.Claims) .ExecuteAsync();
Branch on the exception type (MsalUiRequiredException) and on whether Claims is set — don't parse the AADSTS code out of the message.
For the full pattern, including the failure scenario and client-side handling, see Handling multi-factor auth (MFA), conditional access and incremental consent.
AADSTS50013 — Assertion validation failed
Symptoms
MsalServiceException with error code AADSTS50013: Assertion failed signature validation or invalid_grant.
Common causes
- The incoming token (user assertion) has expired.
- The token was issued by a different authority than expected.
- The audience (
aud) of the token doesn't match the app's client ID or app ID URI.
Resolution
- Verify that the access token passed to
.WithUserAssertion()is fresh and intended for your API. - Ensure your API's app registration has the correct
accessTokenAcceptedVersion(v2 tokens useapi://{clientId}as audience). - Check that the authority in your
ConfidentialClientApplicationmatches the token issuer's tenant.
AADSTS65001 — Consent not granted for OBO
Symptoms
MsalServiceException with error code AADSTS65001 when calling AcquireTokenOnBehalfOf.
Common causes
The downstream API scopes haven't been consented to by the user or admin. OBO requires that the user (or a tenant admin) has granted consent for the downstream permissions.
Resolution
Ensure the required downstream API permissions are declared in your app registration under API permissions.
For multi-tenant apps, trigger admin consent using the admin consent URL:
https://login.microsoftonline.com/{tenant}/adminconsent?client_id={clientId}&redirect_uri={redirectUri}The
redirect_urivalue is required and must be URL-encoded and exactly match one of the reply URLs registered on your app registration under Authentication. Microsoft Entra ID redirects the admin back to this URL after consent is granted or denied, so the request fails if the value is missing or doesn't match a registered reply URL.For single-tenant apps, have a tenant admin grant consent through the Microsoft Entra admin center.
OBO token too large
Symptoms
HTTP 431 (Request Header Fields Too Large) from downstream APIs, or MsalServiceException indicating the token response is too large.
Common causes
Users with many group memberships produce large tokens. When OBO exchanges these, the resulting token can exceed HTTP header size limits.
Resolution
- Configure your app registration to use groups claims with a filter or switch to
hasgroups/_claim_namesclaims (which return a Graph URL instead of embedding all groups). - Use the groups overage pattern to query Microsoft Graph for group membership at runtime.
Client credential errors
Important
Client secrets are the least secure form of client credential. They're easy to leak, they expire, and rotating them is a manual, outage-prone process. Prefer, in order:
- Managed identity when your app runs on Azure — there's no credential to store or rotate.
- Federated identity credentials (workload identity federation) when your app runs outside Azure, such as in GitHub Actions or another cloud.
- Certificates or signed client assertions when neither of the above is available.
Use a client secret only for local development or prototyping, and never check one into source control.
AADSTS7000215 — Invalid client secret
Symptoms
MsalServiceException with AADSTS7000215: Invalid client secret provided.
Resolution
- Verify the secret value (not the secret ID) is used in your configuration.
- Check that the secret hasn't expired in the Microsoft Entra admin center under Certificates & secrets.
- Ensure there are no trailing whitespace or encoding issues when loading the secret from configuration/Key Vault.
- Consider moving off secrets entirely, as described in the preceding note.
AADSTS700024 — Client assertion expired
Symptoms
MsalServiceException with AADSTS700024: Client assertion is not within its valid time range.
Common causes
The certificate used to sign client assertions has expired, or the system clock is significantly skewed.
Resolution
- Check certificate expiration: ensure the certificate's
NotAfterdate hasn't passed. - Verify system clock synchronization (NTP).
- If using Azure Key Vault for certificates, ensure your app is loading the latest version. See Certificate rotation for best practices.
AADSTS700016 — Application not found
Symptoms
MsalServiceException with AADSTS700016: Application with identifier '{clientId}' was not found in the directory '{tenant}'.
Common causes
- Wrong
ClientIdin configuration. - The app registration exists in a different tenant than the authority being used.
- For multi-tenant apps, the app hasn't been consented to in the target tenant.
Resolution
- Double-check the
ClientIdandTenantIdin your configuration. - If using
/commonor/organizationsauthority, ensure the app supports multi-tenant access.
Token cache miss diagnosis
Symptoms
AuthenticationResult.AuthenticationResultMetadata.TokenSource consistently returns IdentityProvider instead of Cache, even for repeated calls with the same parameters.
Diagnostic steps
Check
CacheRefreshReasonon theAuthenticationResultMetadata:Value Meaning NoCachedAccessTokenNo matching token in cache — first call or cache was cleared ExpiredCached token expired (normal for tokens > 1 hour old) ProactivelyRefreshedToken is being refreshed before expiry (normal, improves availability) ForceRefreshOrClaimsApp explicitly called .WithForceRefresh(true)or passed claimsVerify cache key alignment. Tokens are cached by: authority + client ID + scopes + (for OBO) user assertion hash. If any of these differ between calls, you get a cache miss.
Check for accidental
WithForceRefresh(true)in your code — this bypasses the cache entirely.For distributed caches (Redis, SQL): Ensure the serialization callbacks (
SetBeforeAccessAsync/SetAfterAccessAsync) are registered and not throwing silently.// Verify cache callbacks are firing app.AppTokenCache.SetBeforeAccessAsync(async args => { logger.LogDebug("Cache read for {SuggestedKey}", args.SuggestedCacheKey); // Load from distributed store });
Managed Identity failures
IMDS timeout or unavailable
Symptoms
MsalServiceExceptionwith error message indicating IMDS (Instance Metadata Service) is unreachable.- Long delays (2+ seconds) before token acquisition fails.
HttpRequestExceptionorTaskCanceledExceptionduring managed identity calls.
Common causes
- The application is not running in an Azure environment that supports managed identity (e.g., running locally or in an unsupported hosting environment).
- Network Security Group (NSG) rules block access to the IMDS endpoint (
169.254.169.254). - A user-assigned managed identity ID is specified but doesn't exist or isn't assigned to the resource.
Resolution
Verify the hosting environment supports managed identity (App Service, Azure Functions, VMs, AKS, Container Apps, etc.).
For local development, use
DefaultAzureCredentialfromAzure.Identitywhich falls through to developer credentials when MI is unavailable — or use environment variables to disable MI locally.Check NSG rules — ensure outbound access to
169.254.169.254:80is allowed.For user-assigned MI, verify the
ManagedIdentityIdvalue matches the client ID, resource ID, or object ID of an identity assigned to your Azure resource:var miApp = ManagedIdentityApplicationBuilder .Create(ManagedIdentityId.WithUserAssignedClientId("your-client-id")) .Build();
AADSTS70021 — No matching federated identity record
Symptoms
MsalServiceException with AADSTS70021 when using workload identity federation with managed identity.
Resolution
- Verify the federated identity credential is configured correctly on the target app registration.
- Check that the
subject,issuer, andaudiencevalues in the federated credential match what the managed identity token contains.