Federated identity credential errors (workload identity federation)
Look up a workload identity federation failure — AADSTS70021, 700211 (issuer), 700212 (audience), 700213 and 7002138 (subject, case), 700229, client-assertion errors 700021/700023/50027, the GitHub "Failed to fetch federated token" error, 409 on concurrent creation — with the rule each one breaks, the check that shows the presented claim against the credential, and the fix, including GitHub's 2026 immutable subject format.
markdownreferencev1BENQ-6PRTstage: operatetrust: officialservice: entrainterface: cliinterface: sdkinterface: ciAADSTS70021AADSTS700211AADSTS700212AADSTS700213AADSTS7002138AADSTS700229AADSTS700021AADSTS700023AADSTS50027No matching federated identity record found for presented assertioncase-sensitive comparisonfederated identity credentialworkload identity federationGitHub Actions OIDC subjectimmutable subjectFailed to fetch federated token from GitHubid-token writeapi://AzureADTokenExchangefederated credential 409 conflict20 federated credentials limit
Federated identity credential errors
A federated credential trusts an external token when four values match exactly: issuer = the token's iss, subject = its sub, one of audiences = its aud, and the token is signed RS256. Matching has been case-sensitive since September 2024, wildcards aren't allowed (except in preview flexible credentials), and a wrong value saves without complaint: the error appears only at token exchange.
The check for every row: put the presented claims next to the credential.
- Presented claims.
azure/loginprints them in the run log underFederated token details:(issuer,subject claim,audience,job_workflow_ref). The AADSTS700213 message also quotes the subject. Elsewhere, decode the external token's payload. - Credential on an app:
az ad app federated-credential list --id <client-id>. - Credential on a user-assigned managed identity:
az identity federated-credential list --identity-name <name> --resource-group <rg>.
Errors
| Error | Cause → fix | Check |
|---|---|---|
AADSTS70021: No matching federated identity record found for presented assertion. | (1) The credential was just created: it takes minutes to propagate, so wait and retry, and keep retry logic on token requests. (2) No credential on this app or identity matches at all: wrong client ID in the workflow | The list call: is there a credential with this issuer? Is AZURE_CLIENT_ID the app or identity that holds it? |
AADSTS700211: No matching federated identity record found for presented assertion issuer … | The credential's issuer differs from the token's iss. GitHub's is https://token.actions.githubusercontent.com (one Learn page shows a trailing slash; the configuration rule and examples don't). Leading or trailing whitespace blocks the exchange. On AKS identity bindings, set enableAzureProxy: true on WorkloadIdentityCredential (AKS child hub) | issuer in the list output vs the token's iss |
AADSTS700212: No matching federated identity record found for presented assertion audience '…' | The token's aud isn't in the credential's audiences. Use api://AzureADTokenExchange on both sides; azure/login's audience input defaults to it | audiences vs the token's aud |
AADSTS700213: No matching federated identity record found for presented assertion subject '<sub>'. Please note that matching is done using a case-sensitive comparison. … | The subject differs. See Subjects below: environment vs branch, pull request, tag, and the immutable format | The quoted <sub> vs each credential's subject |
AADSTS7002138: … The subject matches with case-insensitive comparison, but not with case-sensitive comparison. | Only the casing differs (organization, repository, branch or environment name). Copy the subject from the run log exactly | Same comparison, character by character |
AADSTS700229 ForbiddenTokenType | A credential that trusts a Microsoft Entra issuer (an app trusting a managed identity) was given a user-delegated token. Only app-only tokens are accepted | How the presented token was obtained: it must come from a client-credentials (app-only) flow, so it carries no scp claim |
AADSTS700021 | Self-made client assertion: its sub and iss must both equal the client ID | Decode your JWT |
AADSTS700023 | Client assertion aud must be https://login.microsoftonline.com/<tenant-id>/v2 (as the SDK guide states it) | Decode your JWT |
AADSTS50027 | The JWT assertion is malformed | Decode it; three base64url parts |
Failed to fetch federated token from GitHub. Please make sure to give write permissions to id-token in the workflow. | The job can't request an OIDC token. Add permissions: id-token: write (and contents: read) at workflow or job level | The workflow's permissions block for that job |
| HTTP 409 Conflict creating credentials | Several credentials created concurrently on one user-assigned identity. Create them serially (dependsOn, or a serial copy loop; Terraform azurerm ≥ 3.40.0 does this) | Did the deployment create them in parallel? |
| Can't add another credential | The limit is 20 per app or user-assigned identity. Use GitHub environments to share one subject, or a flexible credential (preview) | Count the list output |
Subjects (GitHub Actions)
GitHub's sub depends on how the job runs (Learn, "Configure an app to trust an external identity provider"):
| The job… | sub |
|---|---|
declares environment: production | repo:<org>/<repo>:environment:production — the environment wins over the branch |
runs on a pull_request event | repo:<org>/<repo>:pull-request |
| runs on a branch push | repo:<org>/<repo>:ref:refs/heads/<branch> |
| runs on a tag push | repo:<org>/<repo>:ref:refs/tags/<tag> |
Immutable subjects (GitHub.com, 2026): GitHub can put the owner and repository IDs in the subject: repo:<org>@<org-id>/<repo>@<repo-id>:ref:refs/heads/main. Repositories created after July 15, 2026, and any repository renamed or transferred after that date, use it automatically; older repositories only after opting in. So AADSTS700213 now has two new causes:
- A new or renamed repository presents the immutable subject, and the credential holds the name-based one. Add a credential with the subject from the run log.
- The credential was made in the portal with the optional owner and repository ID fields filled in (immutable format), but the repository hasn't opted in and still presents the name-based subject (Azure/login troubleshooting).
Migrating without downtime: add the new credential beside the old one, opt the repository in, run the workflow, then az ad app federated-credential delete --id <client-id> --federated-credential-id <old-id>. Not available on GitHub Enterprise Server. Flexible credentials for GitHub must match sub and repository_id or repository_owner_id.
When none of these fits
Compare all four claims, not just the subject, and confirm which client ID the workflow sends. If the error names no claim, wait several minutes after any credential change and retry before changing anything else. Setup from scratch: guides/entra/github-actions-oidc.md; non-federated token errors: reference/entra/errors.md.