# AADSTS7000216: No client credential in a client_credentials request (fix)

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

**Short answer:** AADSTS7000216 means a token request used grant_type=client_credentials but carried no client_secret, no client_assertion and no request parameter, so Entra had nothing to authenticate the app with. Microsoft's error reference doesn't list it. It is a developer error with low attack value. Send the app's secret or a signed client assertion.

**Fix:** Developer: add the credential to the POST body. With a secret, send `client_secret`. With a certificate or federated credential, send `client_assertion` plus `client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer`.

| Field | Value |
|---|---|
| Code | AADSTS7000216 |
| Category | App configuration |
| Type | Failure |
| Attack signal | Low: usually configuration or user error |
| Where to look | `AADServicePrincipalSignInLogs` |
| In Microsoft's error reference | No (checked 2026-10-30) |
| Verification | lab-reproduced (2026-10-30) |
| Last updated | 2026-10-30 |

## What does AADSTS7000216 mean?

Microsoft's [error code reference](https://learn.microsoft.com/entra/identity-platform/reference-error-codes) doesn't list AADSTS7000216 (checked 30 October 2026). We found no other Microsoft page that names it. The wording below is from a lab run:

> AADSTS7000216: 'client_assertion', 'client_secret' or 'request' is required for the 'client_credentials' grant type.

A client-credentials request is an app signing in as itself, so the only thing proving who it is, is the credential in the POST body. 7000216 means there wasn't one.

Microsoft's client credentials flow page gives three shapes of that request, and each needs a credential:

| Credential | Parameters Microsoft lists |
|---|---|
| Shared secret | `client_id`, `scope`, `client_secret`, `grant_type=client_credentials` |
| Certificate | `client_id`, `scope`, `client_assertion_type`, `client_assertion`, `grant_type=client_credentials` |
| Federated credential | `client_assertion` from an external identity provider |

Microsoft's own note on the certificate form: *"The parameters for the certificate-based request differ in only one way from the shared secret-based request: the `client_secret` parameter is replaced by the `client_assertion_type` and `client_assertion` parameters."* `client_assertion_type` has one accepted value, `urn:ietf:params:oauth:client-assertion-type:jwt-bearer`.

The error message also names a third option, `request`. That parameter is not in any of the three tables on Microsoft's client credentials page, so we can't tell you from Microsoft's documentation what Entra accepts in it. Our reading, from the name and the company it keeps in the message, is that it is a signed request object carrying the parameters as a JWT. Unverified, and not something you need in order to fix this error.

### 7000216 against 7000218
[7000218](https://brambrinkmeier.com/entra/aadsts/7000218/) is the same mistake on a different grant. Microsoft lists it as *"The request body must contain the following parameter: 'client_assertion' or 'client_secret'."* In our lab the two separated cleanly:

| Request | Code |
|---|---|
| `grant_type=client_credentials`, no credential | **7000216** |
| Password grant (ROPC) by a confidential client, no credential | [7000218](https://brambrinkmeier.com/entra/aadsts/7000218/) |

So if you are reading a log and want to know which flow failed, 7000216 says the caller was doing client credentials. The rest of the family tells you what happened once a secret *was* sent: [7000215](https://brambrinkmeier.com/entra/aadsts/7000215/) means the secret was wrong, [7000222](https://brambrinkmeier.com/entra/aadsts/7000222/) means it had expired, and [700016](https://brambrinkmeier.com/entra/aadsts/700016/) means the app ID isn't in that tenant at all.

Whether Entra treats the caller as a confidential client matters for 7000218 and is explained in Microsoft's troubleshooting article for that code: a **Web** redirect URI makes the app confidential, a **Mobile and desktop applications** redirect URI makes it public, and with no redirect URI in the request the **Allow public client flows** setting decides. The client-credentials grant has no public-client variant, so 7000216 doesn't depend on that setting.

## Reproduced in the lab

On 30 October 2026 an automated lab run sent a client-credentials token request with `client_id`, `scope` and `grant_type=client_credentials`, and no credential of any kind. Entra returned:

> AADSTS7000216: 'client_assertion', 'client_secret' or 'request' is required for the 'client_credentials' grant type.

Two rounds of the run produced the same code and the same text, and the run had expected [7000218](https://brambrinkmeier.com/entra/aadsts/7000218/). In the same run a correct secret on the same app returned a token, a wrong secret returned [7000215](https://brambrinkmeier.com/entra/aadsts/7000215/), and a password grant with no secret returned [7000218](https://brambrinkmeier.com/entra/aadsts/7000218/). The message is also complete rather than truncated, which is worth noting because the lab records only the first 160 characters of a message.

No sign-in log row was captured. See [how pages are checked](https://brambrinkmeier.com/methodology/).

## Is it an attack?

Usually not. It is a request that was never going to work, so it is most often a developer or deployment mistake: an empty secret variable, a configuration file that didn't load, or a secret read from a store the process can't reach.

| Pattern | Likely meaning |
|---|---|
| A new job or container fails every run, for one app | The secret is missing from the environment or the config file. Nothing was ever sent. |
| Started right after a deployment, for an app that worked before | The secret variable lost its value, or the key vault reference is unresolved. |
| For one app ID, from an IP you don't own | Someone is probing with your app ID. On its own this reveals nothing to them, but check whether that IP also produced [7000215](https://brambrinkmeier.com/entra/aadsts/7000215/) or a success. |
| Across many app IDs from one IP | Someone enumerating app IDs. Our reading: 7000216 tells the caller nothing about whether an app exists, since the credential check never runs. |

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

Which sign-in table 7000216 lands in is undocumented. Our reading: an app signing in as itself belongs in `AADServicePrincipalSignInLogs`, but since the request has no credential the service principal may not be resolved at all and nothing may be written. Check both possibilities:

```kusto
union withsource=SourceTable SigninLogs, AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs
| where TimeGenerated > ago(30d)
| where ResultType in ("7000216", "7000218") or ResultSignature in ("7000216", "7000218")
| summarize Rows = count(), LastSeen = max(TimeGenerated) by SourceTable, ResultType
```

The filter covers `ResultSignature` as well, because the two tables document the columns differently: for `SigninLogs`, `ResultType` *"provides the 5-6 digit error code that's generated during a sign-in event"*, while `AADServicePrincipalSignInLogs` describes `ResultType` as *"Success or Failure"* and `ResultSignature` as *"the error code, if any, for the sign-in operation"*.

Which app and which source, so you know what to repair:

```kusto
AADServicePrincipalSignInLogs
| where TimeGenerated > ago(7d)
| where ResultType == "7000216" or ResultSignature == "7000216"
| summarize Failures = count(),
            FirstSeen = min(TimeGenerated),
            LastSeen = max(TimeGenerated),
            CredentialTypes = make_set(ClientCredentialType, 5),
            Descriptions = make_set(ResultDescription, 3)
          by AppId, ServicePrincipalName, IPAddress
| sort by Failures desc
```

`ClientCredentialType` is documented as *"the type of client credential used. Examples include client assertion, client secret, etc."* On a request with no credential it is likely empty or absent; that is not documented, so check a row.

Pair it with successes to tell a broken deployment from an unknown caller. An IP that never succeeds for the app is the one to look at:

```kusto
let knownAppIPs =
    AADServicePrincipalSignInLogs
    | where TimeGenerated > ago(30d)
    | where ResultType == "0" or ResultType =~ "Success"
    | distinct AppId, IPAddress;
AADServicePrincipalSignInLogs
| where TimeGenerated > ago(7d)
| where ResultType == "7000216" or ResultSignature == "7000216"
| join kind=leftanti knownAppIPs on AppId, IPAddress
| summarize Attempts = count(), LastSeen = max(TimeGenerated) by AppId, ServicePrincipalName, IPAddress
| sort by Attempts desc
```

A starting threshold: a handful an hour from your own build agents is a configuration bug; a steady stream from an address you don't recognise is worth an investigation.

## How do I fix AADSTS7000216?

**For developers:**
1. Put the credential in the POST body. With a secret: `client_secret=<the secret value>`, URL-encoded. Microsoft: *"The client secret must be URL-encoded before being sent."* Basic authentication in the `Authorization` header is also supported.
2. With a certificate or a federated credential: send `client_assertion=<the JWT>` and `client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer`.
3. Check the request really carries what you think. An empty environment variable produces an empty `client_secret`, which Entra reads as absent. Log the parameter names you send, never the values.
4. Keep `grant_type=client_credentials` and a `scope` that is one resource ending in `/.default`.
5. If the next error is [7000215](https://brambrinkmeier.com/entra/aadsts/7000215/), the credential is now arriving and is wrong, which is progress.

**For admins:**
1. Identify the app from the query above and tell its owner which credential is missing.
2. If the app has no usable credential at all, add one: a certificate is preferable to a secret.
3. If the failures come from an address you don't own, nothing leaked here, but treat it as probing of that app ID and check for [7000215](https://brambrinkmeier.com/entra/aadsts/7000215/) or successes from the same address.

## Evidence

Reproduced in a lab tenant on 2026-10-30 (Client-credentials token request against a lab tenant; scenario: Confidential client sends no credential). First line returned:

> AADSTS7000216: 'client_assertion', 'client_secret' or 'request' is required for the 'client_credentials' grant type.

Lab-run rows naming this code (7; full list at https://brambrinkmeier.com/lab-runs.json):

| Date | Scenario | Expected | Got | Message (first line) |
|---|---|---|---|---|
| 2026-10-30 | Confidential client sends no credential | 7000218 | 7000216 | AADSTS7000216: 'client_assertion', 'client_secret' or 'request' is required for the 'client_credentials' grant type. |
| 2026-10-30 | Confidential client sends no credential | 7000218 | 7000216 | AADSTS7000216: 'client_assertion', 'client_secret' or 'request' is required for the 'client_credentials' grant type. |
| 2026-10-30 | Confidential client sends no credential | 7000218 | 7000216 | AADSTS7000216: 'client_assertion', 'client_secret' or 'request' is required for the 'client_credentials' grant type. |
| 2026-10-30 | Confidential client sends no credential | 7000218 | 7000216 | AADSTS7000216: 'client_assertion', 'client_secret' or 'request' is required for the 'client_credentials' grant type. |
| 2026-10-30 | Confidential client sends no credential | 7000218 | 7000216 | AADSTS7000216: 'client_assertion', 'client_secret' or 'request' is required for the 'client_credentials' grant type. |
| 2026-10-31 | Confidential client sends no credential | 7000218 | 7000216 | AADSTS7000216: 'client_assertion', 'client_secret' or 'request' is required for the 'client_credentials' grant type. |
| 2026-10-31 | Confidential client sends no credential | 7000218 | 7000216 | AADSTS7000216: 'client_assertion', 'client_secret' or 'request' is required for the 'client_credentials' grant type. |

## Sources

1. [Microsoft Entra authentication and authorization error codes](https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes)
2. [Microsoft identity platform and the OAuth 2.0 client credentials flow](https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-client-creds-grant-flow)
3. [Error AADSTS7000218 when a confidential client application authenticates to Microsoft Entra ID](https://learn.microsoft.com/en-us/troubleshoot/entra/entra-id/app-integration/confidential-client-application-authentication-error-aadsts7000218)
4. [Microsoft identity platform application authentication certificate credentials](https://learn.microsoft.com/en-us/entra/identity-platform/certificate-credentials)
5. [AADServicePrincipalSignInLogs table reference (Azure Monitor)](https://learn.microsoft.com/en-us/azure/azure-monitor/reference/tables/aadserviceprincipalsigninlogs)

## Related codes

[AADSTS7000218](https://brambrinkmeier.com/entra/aadsts/7000218/), [AADSTS7000215](https://brambrinkmeier.com/entra/aadsts/7000215/), [AADSTS7000222](https://brambrinkmeier.com/entra/aadsts/7000222/), [AADSTS700016](https://brambrinkmeier.com/entra/aadsts/700016/), [AADSTS50027](https://brambrinkmeier.com/entra/aadsts/50027/), [AADSTS90023](https://brambrinkmeier.com/entra/aadsts/90023/)

## 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, "AADSTS7000216: No client credential in a client_credentials request (fix)", https://brambrinkmeier.com/entra/aadsts/7000216/ (CC BY 4.0), last updated 2026-10-30.
