# AADSTS50196: App is stuck in a sign-in loop (fix)

Canonical: https://brambrinkmeier.com/entra/aadsts/50196/
By Bram Brinkmeier. JSON: https://brambrinkmeier.com/api/aadsts/50196.json

**Short answer:** AADSTS50196 means Entra detected a client loop: the app sent the same request too many times in a short period. Microsoft says the usual cause is missing or broken token caching. It points to a faulty app, not usually to an attacker. The fix is in the app: cache tokens, call the silent method first, and stop retrying on errors.

**Fix:** Developer: set up token caching, call `AcquireTokenSilent` before any interactive call, don't retry in a tight loop, and honour `Retry-After` for app-only tokens. User: close the app or tab and reopen it, or sign out and back in. If it keeps happening, report it to the app owner.

| Field | Value |
|---|---|
| Code | AADSTS50196 |
| Microsoft name | LoopDetected |
| Category | Tokens and sessions |
| Type | Failure |
| Attack signal | Low: usually configuration or user error |
| Where to look | `SigninLogs`, `AADNonInteractiveUserSignInLogs`, `AADServicePrincipalSignInLogs` |
| In Microsoft's error reference | Yes |
| Verification | documentation |
| Last updated | 2026-10-30 |

## What does AADSTS50196 mean?

Microsoft's description:

> **LoopDetected**: A client loop has been detected. Check the app’s logic to ensure that token caching is implemented, and that error conditions are handled correctly. The app has made too many of the same request in too short a period, indicating that it is in a faulty state or is abusively requesting tokens.

Entra is throttling one client. The app asked for the same token over and over, and Entra stopped answering it with tokens.

Microsoft's MSAL.NET throttling page says when this happens: *"Microsoft Entra ID throttles applications when you call the authentication API too frequently. Most often this happens when token caching is not used."* It also says how it shows up. For user-facing calls, the app gets an `invalid_grant` error with the message *"AADSTS50196: The server terminated an operation because it encountered a loop while processing a request"*. For the client credentials grant (app-only tokens), Entra replies with `429 Too Many Requests` and a `Retry-After: 60` header instead. Our reading: an app-only loop may therefore not show up as 50196 at all, but as throttling with no AADSTS code in the client.

## Is it an attack?

Usually not. Microsoft's description allows two readings: the app *"is in a faulty state or is abusively requesting tokens."* In practice the first is far more common, and the cause sits in the app's code.

| Pattern | Likely meaning |
|---|---|
| One app, many users, starting on the day of a release | A bug in the new version: token cache broken, or a retry loop. |
| One user, one app, one device | That device's token cache is broken or the app is stuck after an error. Often a browser tab or desktop client stuck in a redirect loop. |
| A script or tool (PowerShell, CLI, a sync job) | The script asks for a new token on every call instead of reusing one. |
| One IP, many users, an app you don't recognise | Worth a look, though Microsoft doesn't describe this case. It could be an automated tool replaying tokens at scale. Check what else that IP did. |

Most likely, 50196 is a symptom of request volume, not of a credential being guessed: the user was already signed in.

## How do I find it in my logs (KQL)?

Microsoft doesn't document which sign-in table 50196 is logged in. Check where it lands in your workspace:

```kusto
union withsource=SourceTable SigninLogs, AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs
| where TimeGenerated > ago(7d)
| where ResultType == "50196"
| summarize Rows = count(), Apps = dcount(AppId) by SourceTable
```

Which apps, users and clients loop:

```kusto
union SigninLogs, AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(7d)
| where ResultType == "50196"
| summarize Rows = count(),
            Users = dcount(UserPrincipalName),
            IPs = dcount(IPAddress),
            Agents = make_set(UserAgent, 5),
            FirstSeen = min(TimeGenerated),
            LastSeen = max(TimeGenerated)
          by AppDisplayName, AppId, ResourceDisplayName
| sort by Rows desc
```

A new looping app or a sudden spike. This compares the last day with the 14 days before it:

```kusto
let recent =
    union SigninLogs, AADNonInteractiveUserSignInLogs
    | where TimeGenerated > ago(1d)
    | where ResultType == "50196"
    | summarize RecentRows = count(), RecentUsers = dcount(UserPrincipalName) by AppId, AppDisplayName;
let baseline =
    union SigninLogs, AADNonInteractiveUserSignInLogs
    | where TimeGenerated between (ago(15d) .. ago(1d))
    | where ResultType == "50196"
    | summarize BaselineRows = count() by AppId;
recent
| join kind=leftouter baseline on AppId
| extend BaselineRows = coalesce(BaselineRows, 0)
| where BaselineRows == 0 or RecentRows > 3 * (BaselineRows / 14.0)
| project AppDisplayName, AppId, RecentRows, RecentUsers, BaselineRows
| sort by RecentRows desc
```

The factor of 3 is a starting point. Non-interactive rows are aggregated when application, user, IP address, status and resource ID match, so a single row can stand for many requests. Count rows to compare apps, not to count requests.

## How do I fix AADSTS50196?

**For the user:** close the app or browser tab completely and open it again. If that doesn't help, sign out of the app, clear its cached sign-in, and sign in again. If it keeps happening, report it to whoever runs the app; the fix is in the app.

**For developers:** Microsoft's throttling page lists the usual causes:
1. *"Token caching is not setup correctly."*
2. Not calling the silent method (`AcquireTokenSilent`) before an interactive or username/password call.
3. Asking for a scope that doesn't apply to personal Microsoft accounts, such as `User.ReadBasic.All`, *"resulting in cache misses."*

Reuse tokens until they are close to expiry. Don't retry in a tight loop when a call fails. For app-only tokens, honour the `Retry-After` header. MSAL throws `MsalThrottledServiceException` or `MsalThrottledUiRequiredException` when it throttles on the client side; the page notes that *"If MSAL would not apply client-side throttling the application would still not be able to acquire tokens."*

**For admins:**
1. Use the second query to find the app, then give its owner the time window and the user agents.
2. If it is a script running under a user account, check that it gets one token and reuses it.
3. If an unknown app or a single IP produces 50196 for many users, look at that IP's other sign-ins before you close it as a bug.

## Evidence

Not reproduced in the lab yet; the page is based on Microsoft's documentation.

## Sources

1. [Microsoft Entra authentication and authorization error codes](https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes)
2. [Client and server throttling (MSAL.NET)](https://learn.microsoft.com/en-us/entra/msal/dotnet/advanced/client-and-server-throttling)
3. [Non-interactive user sign-ins](https://learn.microsoft.com/en-us/entra/identity/monitoring-health/concept-noninteractive-sign-ins)
4. [SigninLogs table reference (Azure Monitor)](https://learn.microsoft.com/en-us/azure/azure-monitor/reference/tables/signinlogs)

## Related codes

[AADSTS50058](https://brambrinkmeier.com/entra/aadsts/50058/), [AADSTS70043](https://brambrinkmeier.com/entra/aadsts/70043/), [AADSTS700082](https://brambrinkmeier.com/entra/aadsts/700082/), [AADSTS54005](https://brambrinkmeier.com/entra/aadsts/54005/)

## License and citation

CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/). CC BY 4.0 covers the author's own text and classification fields. Quoted Microsoft error wording belongs to Microsoft, is quoted with its source and can change.

Cite as: Bram Brinkmeier, "AADSTS50196: App is stuck in a sign-in loop (fix)", https://brambrinkmeier.com/entra/aadsts/50196/ (CC BY 4.0), last updated 2026-10-30.
