AADSTS7000216: No client credential in a client_credentials request (fix)
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`. Full steps
| Code | AADSTS7000216 |
|---|---|
| In Microsoft's error reference | No (checked ). Wording on this page is what Entra returned or what users report. |
| Category | App configuration |
| Type | Failure |
| Attack signal | Low: usually configuration or user error |
| Where to look | AADServicePrincipalSignInLogs |
| Microsoft lookup | login.microsoftonline.com/error?code=7000216 |
What does AADSTS7000216 mean?
Section titled “What does AADSTS7000216 mean?”Microsoft’s error code reference 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
Section titled “7000216 against 7000218”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 |
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 means the secret was wrong, 7000222 means it had expired, and 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
Section titled “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. In the same run a correct secret on the same app returned a token, a wrong secret returned 7000215, and a password grant with no secret returned 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.
Is it an attack?
Section titled “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 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)?
Section titled “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:
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, ResultTypeThe 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:
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 descClientCredentialType 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:
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 descA 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?
Section titled “How do I fix AADSTS7000216?”For developers:
- 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 theAuthorizationheader is also supported. - With a certificate or a federated credential: send
client_assertion=<the JWT>andclient_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer. - 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. - Keep
grant_type=client_credentialsand ascopethat is one resource ending in/.default. - If the next error is 7000215, the credential is now arriving and is wrong, which is progress.
For admins:
- Identify the app from the query above and tell its owner which credential is missing.
- If the app has no usable credential at all, add one: a certificate is preferable to a secret.
- 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 or successes from the same address.
Related codes
- AADSTS7000218: Client secret or assertion missing from the request
- AADSTS7000215: Invalid client secret is provided
- AADSTS7000222: The provided client secret keys are expired
- AADSTS700016: Application not found in the directory
- AADSTS50027: JWT token is invalid or malformed
- AADSTS90023: Entra rejected the request as invalid
Sources
- Microsoft Entra authentication and authorization error codes
- Microsoft identity platform and the OAuth 2.0 client credentials flow
- Error AADSTS7000218 when a confidential client application authenticates to Microsoft Entra ID
- Microsoft identity platform application authentication certificate credentials
- AADServicePrincipalSignInLogs table reference (Azure Monitor)