microsoft/aspire · error · InvalidOperationException
The access token returned by the credential is not a valid…
Error message
The access token returned by the credential is not a valid JWT (expected 3 '.'-separated segments, found {parts.Length}). What it means
DefaultAzurePrincipalProvider.GetPrincipalAsync obtains an access token from the configured TokenCredential and decodes it as a JWT to read identity claims (oid, tid, idtyp). A JWT must have three dot-separated segments (header.payload.signature); when the credential returns a token with fewer segments, the provider throws a descriptive InvalidOperationException instead of crashing in the base64 decoder. This indicates the credential returned something that is not a JWT (e.g. an error page or opaque token).
Solutions
- Inspect the credential's token source — fix the credential configuration so it returns a real Entra ID access token (log only the token shape/segment count, never the value).
- Use standard Azure.Identity credentials (DefaultAzureCredential, ClientSecretCredential) instead of custom implementations.
- If behind a corporate proxy, ensure HTTPS auth endpoints are not intercepted/rewritten with error pages.
- In tests, make fake credentials return well-formed 'header.payload.signature' JWTs with a valid base64url payload containing an oid claim.
Example fix
// before
public class FakeCredential : TokenCredential
{
public override AccessToken GetToken(...) => new("not-a-jwt", DateTimeOffset.UtcNow.AddHours(1));
}
// after
public class FakeCredential : TokenCredential
{
private const string Payload = "eyJvaWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDAifQ"; // {"oid":"00000000-..."}
public override AccessToken GetToken(...) => new($"header.{Payload}.signature", DateTimeOffset.UtcNow.AddHours(1));
} Defensive patterns
Strategy: validation
Validate before calling
var parts = token.Split('.');
if (parts.Length < 3)
throw new InvalidOperationException("Credential returned a non-JWT token; check the credential configuration/auth endpoint."); Type guard
bool LooksLikeJwt(string token) =>
!string.IsNullOrWhiteSpace(token) && token.Split('.').Length >= 3; Try / catch
try
{
var principal = await principalProvider.GetPrincipalAsync(ct);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("not a valid JWT"))
{
// replace/repair the credential so it returns a real Entra ID access token
} Prevention
- Use standard Azure.Identity credentials rather than custom TokenCredential implementations.
- Never log full tokens; log only segment counts when diagnosing token shape issues.
- In tests, have fake credentials mint well-formed header.payload.signature JWTs with base64url payloads.
- Watch for corporate proxies rewriting auth endpoint responses into error pages.
When it happens
Trigger: Calling GetPrincipalAsync when TokenCredential.GetTokenAsync returns a string that is not a JWT — for example a credential whose token endpoint returned an HTML error response, a test/fake credential returning arbitrary text, or a misconfigured custom TokenCredential.
Common situations: Developers hit this with custom or mocked TokenCredential implementations, proxies/interceptors that mangle auth responses, corporate proxies returning error bodies, or auth endpoints returning non-token content that the credential passes through.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Unable to determine the Azure identity to provision as: the…
- Existing Azure sandbox group
- A BlobServiceClient could not be configured. Ensure valid…
- A BlobServiceClient could not be configured. Ensure valid…
- A ChatCompletionsClient could not be configured. Ensure…
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/89cf0db807b48b9e.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting.Azure/Provisioning/Internal/DefaultAzurePrincipalProvider.cs:39
// Values accepted by the `principalType` property on Microsoft.Authorization/roleAssignments.
// We don't emit "Group" here because access tokens never represent a group identity directly.
private const string PrincipalTypeUser = "User";
private const string PrincipalTypeServicePrincipal = "ServicePrincipal";
public async Task<AzurePrincipal> GetPrincipalAsync(CancellationToken cancellationToken = default)
{
var credential = tokenCredentialProvider.TokenCredential;
var response = await credential.GetTokenAsync(new(["https://graph.windows.net/.default"]), cancellationToken).ConfigureAwait(false);
static AzurePrincipal ParseToken(in AccessToken response)
{
// A JWT is "header.payload.signature". The token credential should always return
// that shape, but guard explicitly so a malformed token surfaces as a clear error
// instead of a confusing IndexOutOfRangeException deep in the parser.
var parts = response.Token.Split('.');
if (parts.Length < 3)
{
throw new InvalidOperationException(
$"The access token returned by the credential is not a valid JWT (expected 3 '.'-separated segments, found {parts.Length}).");
}
// Decode the JWT payload (the middle segment). JWTs use base64url with stripped
// padding (RFC 7515 §2), so swap the URL-safe characters back and re-pad to a length
// divisible by four before base64-decoding. Example payload shape:
// { "oid":"<guid>","upn":"user@contoso.com","idtyp":"user","iss":"..." }
// For app-only (service principal) tokens the `upn` claim is absent and `idtyp` is "app".
var part = parts[1];
var convertedToken = part.Replace('_', '/').Replace('-', '+');
switch (part.Length % 4)
{
case 2:
convertedToken += "==";
break;
case 3:
convertedToken += "=";View on GitHub (pinned to 25830f84bd)