When a daemon, pipeline or integration stops getting tokens from Microsoft Entra ID, the error usually lands as an AADSTS code in a log nobody reads until something downstream breaks. This guide covers the three codes that cause most service principal outages, AADSTS7000215 (invalid client secret), AADSTS7000222 (expired client secret) and AADSTS700016 (application not found in the tenant), plus the neighbouring codes that are often confused with them. For each one it gives the symptom, the likely causes from most to least common, where to look to confirm the cause, the fix and how to stop it happening again. It is written for identity engineers, platform and DevOps teams, and the people on call when an app-only integration fails at 2am.
Key takeaways
- Read the token endpoint response first. The JSON error body carries
error,error_description,error_codes,trace_idandcorrelation_id, which is everything you need to find the matching event in the service principal sign-in log. - 7000215 and 7000222 are credential problems, not permission problems. The wrong secret string, a secret that has passed its end date, or a secret from a different app registration produces these codes before any authorisation is evaluated.
- 700016 means Entra cannot find the client in the tenant you asked. Check the client ID, the tenant in the token URL, and whether a service principal exists in that tenant, in that order.
- Fix the cause, not only the symptom. Rotating a secret ends today's outage; moving the workload to a certificate, managed identity or workload identity federation removes the expiry class of failure entirely.
- Alert on credential expiry before it becomes an error. Every 7000222 is a missed expiry warning, so track end dates on every credential and page the owning team well ahead of the date.
How app-only sign-in works and where it breaks
A service principal signs in with the OAuth 2.0 client credentials grant. The workload sends a POST to https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token with client_id, grant_type=client_credentials, a scope of the form https://graph.microsoft.com/.default, and one credential: a URL-encoded client_secret, or a signed client_assertion JWT with client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer. Microsoft documents that no refresh token is issued in this flow, so every token renewal is a fresh credential check, which is why a bad or expired credential surfaces within about an hour of the change.
The request passes through four checks, and each failure maps to a family of codes:
- Tenant resolution: the
{tenant}value must be a tenant that exists in this cloud (AADSTS90002 when it does not). - Client lookup: the
client_idmust match an application with a service principal in that tenant (AADSTS700016), and the application must be enabled (AADSTS7000112). - Credential validation: the secret must match a current password credential (AADSTS7000215, AADSTS7000222), or the assertion must validate against a registered certificate (AADSTS700027). A missing credential gives AADSTS7000218.
- Resource and policy: the scope must be valid (AADSTS70011), the resource must exist in the tenant (AADSTS500011), and Conditional Access for workload identities can still block issuance (AADSTS53003; see the Conditional Access Block Troubleshooting Guide (AADSTS53003)).
Knowing which stage failed narrows the search immediately. A credential error tells you the tenant and client were found; a 700016 tells you the credential was never examined.
Error reference: the codes and what they mean
The descriptions below follow Microsoft's error code reference as read on 11 November 2026. Microsoft states that codes and messages can change at any time and that applications should branch on the error field rather than on the description text, so use the live lookup at https://login.microsoftonline.com/error?code=7000215 (replace the number) to confirm the current wording.
| Code | Name or meaning (Microsoft reference) | Stage that failed | First thing to check |
|---|---|---|---|
| AADSTS7000215 | Invalid client secret is provided | Credential validation | Is the value sent the secret Value, for this app, unmodified and URL-encoded? |
| AADSTS7000222 | InvalidClientSecretExpiredKeysProvided: the provided client secret keys are expired | Credential validation | End date of the secret on the app registration |
| AADSTS700016 | UnauthorizedClient_DoesNotMatchRequest: the application wasn't found in the directory/tenant | Client lookup | Client ID, tenant in the URL, service principal present in the tenant |
| AADSTS7000218 | Request body must contain 'client_assertion' or 'client_secret' | Credential validation | Empty variable or secret not injected at runtime |
| AADSTS700027 | Client assertion failed signature validation | Credential validation | Certificate registered, thumbprint header, signing key |
| AADSTS7000112 | UnauthorizedClientApplicationDisabled: the application is disabled | Client lookup | "Enabled for users to sign in" on the enterprise application |
| AADSTS90002 | InvalidTenantName: the tenant name wasn't found | Tenant resolution | Tenant ID or domain, and the cloud (public, government, sovereign) |
| AADSTS70011 | InvalidScope: the scope requested by the app is invalid | Resource | Scope ends in /.default and targets one resource |
| AADSTS500011 | InvalidResourceServicePrincipalNotFound | Resource | Resource app exists in this tenant |
AADSTS7000215: invalid client secret
Symptom: the token request fails with "error": "invalid_client" and AADSTS7000215 in the description. The service principal sign-in log shows a failure for the right application, which confirms the tenant and client ID are correct.
Likely causes, most to least common
- The Secret ID was used instead of the Value. The Certificates & secrets blade shows both. The Value is shown once at creation; the Secret ID is a GUID that looks plausible in a config file and always fails.
- A stale copy of the secret. Someone rotated the secret in Entra, but the pipeline variable, Key Vault entry, Kubernetes secret or application setting still holds the old value. The old credential was deleted, so the old value is now simply wrong.
- Encoding or whitespace damage. Secrets can contain characters such as
~,.and_, and occasionally characters that break naive string handling. Microsoft's client credentials documentation requires the secret to be URL-encoded in the form body. Trailing newlines fromechoor a file read are a frequent culprit. - A secret from a different app registration. Common where dev, test and prod apps have similar names, or where a multi-tenant app has a separate registration per environment.
- The wrong store or slot at runtime. A deployment slot, environment or Helm values file pointing at another secret reference.
How to diagnose
- Compare the hint, not the secret: the portal shows the first characters of each secret's Value under the Hint column. Compare that prefix with the first characters of what your workload holds, without printing the full secret to a log.
- Check the credential list: on the app registration, list every client secret with its description, Secret ID, start and end date. With Microsoft Graph, read
passwordCredentialson the application object (GET /applications/{object-id}?$select=passwordCredentials) to getkeyId,hint,startDateTimeandendDateTime. - Check the audit log for recent changes: filter the Entra audit log for the application and the activity "Update application: Certificates and secrets management" around the time failures began. A rotation that preceded the first failure points to cause 2.
- Reproduce outside the workload: request a token from a controlled shell with the secret read straight from the store. If that succeeds, the defect is in how the workload loads or encodes the value.
Fix
Create a new secret, copy the Value immediately into the secret store the workload reads from, restart or refresh the workload so it picks up the new value, confirm a successful service principal sign-in, then delete the broken or superseded secret. Never paste the Value into a ticket or chat thread to "check" it. See the Azure Entra Client Secrets Security Guide for storage and rotation patterns.
AADSTS7000222: expired client secret
Symptom: an integration that worked for months fails abruptly at roughly the same time of day as the secret was created, and every instance fails at once. The error names expired keys, and Microsoft's message itself suggests creating new keys or using certificate credentials.
Likely causes
- Nobody owned the expiry date. The secret was created with the default or maximum lifetime and no reminder.
- Rotation created a new secret but the workload still sends the old one. Here the old secret is still on the app and has expired, so you see 7000222 rather than 7000215.
- Multiple secrets, one expired: instances pinned to different secrets fail in part, so the outage looks intermittent.
How to diagnose
- Match the key: compare the expired secret's
endDateTimewith the first failure time in the service principal sign-in log. - Find every consumer: search pipeline variables, Key Vault, configuration repositories and runbooks for the client ID. One secret often feeds more systems than the owner remembers.
- Find siblings at risk: while you are in the tenant, list every other credential expiring in the next 30 days. See How to find expiring app secrets and certificates in Entra ID.
Fix and prevention
Issue a new secret, deploy it to every consumer, verify sign-ins, then remove the expired credential so it cannot cause confusion again. For prevention, the durable answer is to stop using shared secrets where the platform allows: a managed identity for Azure-hosted workloads, a federated identity credential for GitHub Actions, Kubernetes or another cloud, or a certificate held in a key store with automated renewal. Where a secret must stay, set a short lifetime, record an owner, and alert at 30 and 7 days. An application management policy can restrict secret lifetimes or block new secrets tenant-wide.
AADSTS700016: application not found in the directory
Symptom: the error says the application was not found in the directory or tenant, and the service principal sign-in log for your tenant shows nothing, because Entra never matched the request to an object you own.
Likely causes, most to least common
- Wrong tenant in the token URL. The request goes to
/common,/organizations, another customer's tenant, or a test tenant while the app lives elsewhere. - Wrong client ID. The Object ID or the enterprise application's object ID was used instead of the Application (client) ID, or the ID belongs to a deleted registration.
- No service principal in the target tenant. A multi-tenant app has not been consented to or provisioned in the customer tenant. Microsoft's description names this: the app has not been installed by an administrator or consented to by any user.
- Wrong cloud. An app registered in a sovereign or government cloud called against the public login endpoint, or the reverse.
- The app was deleted. A deleted application sits in the deleted items list for a period and can be restored; a new registration has a new client ID.
How to diagnose
- Decode the configuration: confirm the tenant GUID in the URL matches the Directory (tenant) ID on the app's overview page.
- Look up the client ID: in Enterprise applications for the target tenant, search by Application ID. No result means no service principal; for a multi-tenant app, an administrator must grant consent through
https://login.microsoftonline.com/{tenant}/adminconsent?client_id=.... - Check deleted applications: App registrations, Deleted applications, then restore if deletion was a mistake.
Fix
Correct the tenant or client ID in configuration, provision the service principal through admin consent, or restore the deleted app. Do not "fix" 700016 by creating a new app with broad permissions in a hurry; that leaves an orphan with privileges and no owner. The Entra ID Applications and Consent Guide covers the consent model.
Certificate and federated credential failures
Teams that moved off secrets meet a different set of errors. AADSTS700027 means the client assertion failed signature validation. Microsoft's certificate credential documentation specifies the assertion: header typ of JWT and a x5t#S256 thumbprint of the DER-encoded certificate, claims aud set to the tenant's v2.0 token endpoint, iss and sub both equal to the client ID, a unique jti, and nbf, iat and exp, with an expiry kept to minutes. The usual causes are a certificate that was never uploaded to the app (or was replaced), signing with a different private key than the registered public key, a wrong aud, and clock skew on the host. Libraries such as MSAL build the assertion correctly, so hand-built JWTs are the first suspect. For federated identity credentials, the failures are about issuer, subject and audience matching instead; the Multi-Cloud Workload Identity Troubleshooting Guide covers those.
Choosing the fix: rotate, replace or redesign
| Situation | Short-term fix | Lasting fix | Trade-off |
|---|---|---|---|
| Workload runs in Azure (VM, App Service, Functions, AKS) | New secret | Managed identity | No credential to leak or expire; Azure-only |
| Workload in GitHub Actions, Kubernetes, AWS or GCP | New secret | Federated identity credential | Needs trust configuration per issuer and subject |
| On-premises daemon or third-party product that supports certificates | New secret | Certificate in a hardware or software key store with automated renewal | Certificate lifecycle tooling required |
| Vendor product that only accepts a secret | New secret with shortest workable lifetime | Secret in a vault with automated rotation and expiry alerting | Residual leak risk; push the vendor for certificate or federation support |
Small organisations with a handful of integrations can track expiry in a reviewed list; once there are dozens of app registrations, automate discovery through Microsoft Graph and send alerts to the owning team, not to a shared mailbox.
Where identity fits
Every one of these errors is an NHI lifecycle failure surfacing as an outage: a credential with no owner, no expiry tracking or no record of where it is used. The same gaps make the credential attractive to attackers. Valid cloud credentials are ATT&CK T1078.004 (Cloud Accounts), adding a credential to an existing app is T1098.001 (Additional Cloud Credentials), and secrets found in configuration are T1552.001 (Credentials In Files). The Commvault Metallic breach showed how exposed Entra app secrets give an attacker app-only access to customer Microsoft 365 data, which is why a sudden burst of 7000215 from unfamiliar IP addresses deserves a security look, not only a support ticket. See the Azure RBAC and Service Principals Guide.
Detection and measurement
- Where to look: Entra admin center, Monitoring & health, Sign-in logs, Service principal sign-ins tab. Microsoft notes that managed identity sign-ins appear in a separate log, and that rows are aggregated by service principal, status, IP address and resource, so expand a row to see each attempt.
- Export for querying: send service principal sign-in logs through diagnostic settings to a Log Analytics workspace or SIEM; in Log Analytics they land in the
AADServicePrincipalSignInLogstable, where a non-zeroResultTypeis a failure and holds the numeric code. - Alert on change: raise an alert on audit events that add or remove credentials on applications, and correlate with failures that follow.
- Security signal: repeated 7000215 from an IP address the workload never uses suggests someone is trying a leaked or guessed secret.
| Metric | What good looks like |
|---|---|
| Credentials expiring in the next 30 days with no rotation ticket | Zero |
| AADSTS7000222 incidents per quarter | Zero; each one is a missed alert |
| Share of app registrations using secrets rather than certificates, managed identity or federation | Falling quarter on quarter |
| Median time from first 7000215 or 700016 to restored sign-in | Under one hour |
| App registrations with a named, current owner | 100 per cent |
Common failure patterns
- Rotating in Entra before updating consumers: deleting the old secret at the same moment as creating the new one guarantees an outage. Overlap the two, deploy, verify, then delete.
- Logging the secret while debugging: printing the request body to prove the secret is "correct" leaks it to every log reader. Compare hints instead.
- Using /common for app-only flows: client credentials need a specific tenant;
/commonproduces confusing tenant and client errors. - Granting more permissions to make an error go away: none of 7000215, 7000222 or 700016 is caused by missing API permissions, so adding Graph application permissions only widens the blast radius.
- Treating the error text as a contract: code that parses the description string breaks when Microsoft changes the wording, which its documentation says can happen at any time.
- Secrets with no owner: the app was created by someone who left, so no one receives the expiry notice. See How to find and remove stale service principals in Entra ID.
Framework mapping
| Framework | Reference | How it applies here |
|---|---|---|
| NIST SP 800-53 Rev 5 | IA-5 Authenticator Management | Secret and certificate lifetimes, rotation and protection |
| NIST SP 800-53 Rev 5 | AC-2 Account Management | Owner and lifecycle for every service principal |
| NIST SP 800-53 Rev 5 | AU-6 Audit Record Review, Analysis, and Reporting | Reviewing service principal sign-in failures and credential changes |
| NIST CSF 2.0 | PR.AA-01 | Identities and credentials for services are managed |
| NIST CSF 2.0 | DE.CM-03 | Monitoring technology usage for adverse events |
| CIS Controls v8.1 | Control 5 Account Management; Control 8 Audit Log Management | Service account inventory, log collection and review |
| ISO/IEC 27001:2022 Annex A | 5.17 Authentication information; 8.15 Logging | Managing secrets and recording authentication events |
| MITRE ATT&CK v20 | T1078.004, T1098.001, T1552.001 | Abuse of the same credentials these errors involve |
Practitioner checklist
- Capture
error,error_codes,trace_idandcorrelation_idfrom every failed token request. - Find the matching event in Service principal sign-ins before changing anything.
- For 7000215, compare the secret hint with the stored value and check the audit log for a recent rotation.
- For 7000222, find every consumer of the client ID before issuing the new secret.
- For 700016, confirm tenant ID, Application (client) ID and the existence of the service principal, in that order.
- Overlap old and new credentials during rotation and delete the old one only after verified sign-ins.
- Alert on credential expiry at 30 and 7 days and on credential changes to applications.
- Replace secrets with managed identities, federation or certificates wherever the workload supports it.
- Record a named owner for every app registration and service principal.
Standards and references
- Microsoft Learn: Microsoft Entra authentication and authorization error codes
- Microsoft Learn: OAuth 2.0 client credentials flow on the Microsoft identity platform
- Microsoft Learn: Microsoft identity platform application authentication certificate credentials
- Microsoft Learn: Service principal sign-ins in Microsoft Entra ID
- Microsoft Learn: Conditional Access for workload identities
- IETF: RFC 6749 The OAuth 2.0 Authorization Framework
- NIST: SP 800-53 Rev. 5 Security and Privacy Controls
- MITRE ATT&CK: Valid Accounts: Cloud Accounts (T1078.004)
Related NHI Mgmt Group resources: Azure Entra Client Secrets Security Guide, How to find expiring app secrets and certificates in Entra ID, Entra ID Sign-In and Audit Log Detection Guide, Multi-Cloud Workload Identity Troubleshooting Guide.