microsoft/aspire · error · InvalidOperationException

Unknown Azure credential type

Error message

Unknown Azure credential type '{credential.GetType().Name}'.

What it means

When building a stable identity string for an Azure Radius credential, the code switches over the known AzureRadiusCredential subtypes (ServicePrincipal, WorkloadIdentity). A credential of any other subtype has no defined identity, so this InvalidOperationException is thrown as an internal exhaustiveness guard.

Solutions

  1. Use only the supported credential kinds: AzureRadiusCredential.ServicePrincipal or AzureRadiusCredential.WorkloadIdentity.
  2. Align all Aspire.Hosting.Radius and related package versions so the credential type matches what this validator knows.
  3. If you believe a new credential type should be supported, file an issue with the type name shown in the message.

Example fix

// before
var credential = new MyCustomAzureRadiusCredential(tenantId, clientId);
env.WithRadiusCredential(credential);

// after
var credential = new AzureRadiusCredential.ServicePrincipal(tenantId, clientId);
env.WithRadiusCredential(credential);
Defensive patterns

Strategy: type-guard

Validate before calling

// Validate the credential kind before registering
bool IsSupportedAzureCredential(AzureRadiusCredential c) =>
    c is AzureRadiusCredential.ServicePrincipal or AzureRadiusCredential.WorkloadIdentity;
if (!IsSupportedAzureCredential(credential)) throw new ArgumentException($"Unsupported Azure credential type {credential.GetType().Name}");

Type guard

var ok = credential is AzureRadiusCredential.ServicePrincipal or AzureRadiusCredential.WorkloadIdentity;

Try / catch

try { env.WithRadiusCredential(credential); }
catch (InvalidOperationException ex) when (ex.Message.StartsWith("Unknown Azure credential type"))
{
    logger.LogError(ex, "Credential type {Type} is not supported; use ServicePrincipal or WorkloadIdentity.", credential.GetType().Name);
}

Prevention

When it happens

Trigger: Passing a custom or newly-added (or incorrectly cast) subclass of AzureRadiusCredential through WithRadiusCredential into credential-conflict validation, where the switch expression finds no matching pattern.

Common situations: Upgrading the Aspire.Hosting.Radius package while referencing an older/newer AzureRadiusCredential type from a mismatched package version; hand-rolling a subclass of AzureRadiusCredential that the validator does not recognize.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/aeddf74d69ccd3aa. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting.Radius/Publishing/RadCredentialRegisterStep.cs:225

        var distinctIdentities = configured.Select(static c => c.Identity).Distinct(StringComparer.Ordinal).Count();
        if (distinctIdentities <= 1)
        {
            return;
        }

        var envNames = string.Join(", ", configured.Select(static c => $"'{c.Env}'").Distinct(StringComparer.Ordinal));
        throw new InvalidOperationException(
            $"{provider} cloud-provider credentials are registered per Radius installation (global) and " +
            $"are shared across all environments, but environments {envNames} configure different {provider} " +
            "credentials that would overwrite one another. Configure a single shared credential for all " +
            "environments, or deploy them to separate Radius installations. Diagnostic: ASPIRERADIUS011.");
    }

    private static string AzureCredentialIdentity(AzureRadiusCredential credential) => credential switch
    {
        AzureRadiusCredential.ServicePrincipal sp => $"sp|{Canonicalize(sp.TenantId)}|{Canonicalize(sp.ClientId)}",
        AzureRadiusCredential.WorkloadIdentity wi => $"wi|{Canonicalize(wi.TenantId)}|{Canonicalize(wi.ClientId)}",
        _ => throw new InvalidOperationException($"Unknown Azure credential type '{credential.GetType().Name}'."),
    };

    private static string AwsCredentialIdentity(AwsRadiusCredential credential) => credential switch
    {
        // The access key id identifies the principal; it is bound via a parameter, so its
        // resource name is used as a stable proxy without resolving the value here.
        AwsRadiusCredential.AccessKey ak => $"access-key|{ak.AccessKeyId.Resource.Name}",
        AwsRadiusCredential.Irsa irsa => $"irsa|{irsa.IamRoleArn}",
        _ => throw new InvalidOperationException($"Unknown AWS credential type '{credential.GetType().Name}'."),
    };

    // Inputs are validated as GUIDs before reaching here; normalize to the canonical "D"
    // form so differing casing/formatting of the same GUID is not treated as a conflict.
    private static string Canonicalize(string guid)
        => Guid.TryParse(guid, out var parsed) ? parsed.ToString("D") : guid;

    private static async Task<string> ResolveParameterAsync(
        IResourceBuilder<ParameterResource> parameter,

View on GitHub (pinned to 25830f84bd)