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

  1. 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).
  2. Use standard Azure.Identity credentials (DefaultAzureCredential, ClientSecretCredential) instead of custom implementations.
  3. If behind a corporate proxy, ensure HTTPS auth endpoints are not intercepted/rewritten with error pages.
  4. 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

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


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)