microsoft/aspire · error · InvalidOperationException

Unexpected icon variant

Error message

Unexpected icon variant: {iconVariant}

What it means

MapIconVariant converts ApplicationModel.IconVariant (Regular, Filled, or null) to the dashboard proto IconVariant. Null defaults to Regular, but any non-null unknown value has no representation and throws. This guards enum exhaustiveness when converting app-model data to the dashboard view model.

Solutions

  1. Only assign IconVariant.Regular or IconVariant.Filled when annotating resources
  2. Use Enum.IsDefined to validate values read from external data before mapping
  3. Align Aspire.Hosting and dashboard package versions so enums match
  4. Catch InvalidOperationException and fall back to the default (Regular) variant

Example fix

// before
var variant = (IconVariant)storedValue;
// after
var variant = Enum.IsDefined(typeof(IconVariant), storedValue) ? (IconVariant)storedValue : IconVariant.Regular;
Defensive patterns

Strategy: try-catch

Validate before calling

if (iconVariant is not (IconVariant.Regular or IconVariant.Filled or null))
{
    iconVariant = IconVariant.Regular;
}

Type guard

bool IsKnownIconVariant(IconVariant? v) => v is null or IconVariant.Regular or IconVariant.Filled;

Try / catch

try
{
    protoVariant = MapIconVariant(iconVariant);
}
catch (InvalidOperationException ex) when (ex.Message.StartsWith("Unexpected icon variant"))
{
    protoVariant = IconVariant.Regular; // default variant
}

Prevention

When it happens

Trigger: FromSnapshot/ToViewModel encountering an IconVariant value other than Regular, Filled, or null — e.g. an out-of-range cast, a new enum member from a different Aspire version, or manually constructed resource metadata.

Common situations: Custom resource publishers setting icon variants via unchecked casts; mixed Aspire package versions where the enum gained a member; deserialized model data carrying invalid values.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at src/Aspire.Hosting/Dashboard/proto/Partials.cs:166

        {
            return healthStatus switch
            {
                Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus.Healthy => HealthStatus.Healthy,
                Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus.Degraded => HealthStatus.Degraded,
                Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus.Unhealthy => HealthStatus.Unhealthy,
                _ => throw new InvalidOperationException("Unknown health status: " + healthStatus),
            };
        }
    }

    private static IconVariant MapIconVariant(Hosting.ApplicationModel.IconVariant? iconVariant)
    {
        return iconVariant switch
        {
            Hosting.ApplicationModel.IconVariant.Regular => IconVariant.Regular,
            Hosting.ApplicationModel.IconVariant.Filled => IconVariant.Filled,
            null => IconVariant.Regular,
            _ => throw new InvalidOperationException("Unexpected icon variant: " + iconVariant)
        };
    }

    private static ResourceCommandState MapCommandState(Hosting.ApplicationModel.ResourceCommandState state)
    {
        return state switch
        {
            Hosting.ApplicationModel.ResourceCommandState.Enabled => ResourceCommandState.Enabled,
            Hosting.ApplicationModel.ResourceCommandState.Disabled => ResourceCommandState.Disabled,
            Hosting.ApplicationModel.ResourceCommandState.Hidden => ResourceCommandState.Hidden,
            // A newer or malformed state must not terminate WatchResources or become actionable for older clients.
            _ => ResourceCommandState.Hidden
        };
    }
}

View on GitHub (pinned to 25830f84bd)