microsoft/aspire · error · InvalidOperationException

Union input types must define at least one member type.

Error message

Union input types must define at least one member type.

What it means

MapInputUnionTypeToTypeScript maps an input union type reference to TypeScript. A union with a null or empty UnionTypes list cannot produce any member types, so the projector throws this InvalidOperationException as a validation guard before building the union.

Solutions

  1. Populate typeRef.UnionTypes with at least one member AtsTypeRef before projecting.
  2. Fix the analyzer/builder that produced the empty union (usually means it failed to resolve member types).
  3. If the type is genuinely not a union, construct a plain type ref instead of an empty union.
  4. Log the offending typeId to locate where the incomplete AtsTypeRef was created.

Example fix

// before
var typeRef = new AtsTypeRef { UnionTypes = new List<AtsTypeRef>() }; // empty union
var ts = projector.MapInputUnionTypeToTypeScript(typeRef); // throws

// after
typeRef.UnionTypes.Add(new AtsTypeRef { /* string member */ });
typeRef.UnionTypes.Add(new AtsTypeRef { /* number member */ });
var ts = projector.MapInputUnionTypeToTypeScript(typeRef); // "string | number"
Defensive patterns

Strategy: validation

Validate before calling

if (typeRef.UnionTypes is not { Count: > 0 }) {
    throw new InvalidOperationException("populate UnionTypes with at least one member before mapping");
}

Try / catch

try {
    var ts = MapInputUnionTypeToTypeScript(typeRef);
} catch (InvalidOperationException ex) when (ex.Message.Contains("at least one member")) {
    // fix the builder that produced the empty union and retry
    throw;
}

Prevention

When it happens

Trigger: Building an input type projection from an AtsTypeRef whose UnionTypes collection is null or has zero entries — e.g. an API model constructed with a union but no member types added, or deserialized model with an empty union list.

Common situations: Builder code creates an AtsTypeRef for a union but forgets to call Add on UnionTypes; a custom analyzer produces an empty union for a type it could not resolve; hand-edited or deserialized metadata lost the member list.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at src/Aspire.Hosting.CodeGeneration.TypeScript/TypeScriptApiProjector.cs:1898

        if (typeRef?.TypeId == InteractionInputCollectionTypeId)
        {
            return $"Awaitable<{GetInteractionInputCollectionClassName()}>";
        }

        if (IsCancellationTokenType(typeRef))
        {
            return $"AbortSignal | {GetCancellationTokenInterfaceName()}";
        }

        return MapTypeRefToTypeScript(typeRef);
    }

    internal string MapInputUnionTypeToTypeScript(AtsTypeRef typeRef)
    {
        if (typeRef.UnionTypes == null || typeRef.UnionTypes.Count == 0)
        {
            throw new InvalidOperationException("Union input types must define at least one member type.");
        }

        // Build union structurally: each member is mapped individually.
        // Handle types become Awaitable<T>, non-handle types pass through as-is.
        var nonHandleTypes = new List<string>();
        var handleTypeNames = new List<string>();

        foreach (var memberRef in typeRef.UnionTypes)
        {
            if (IsWidenedHandleType(memberRef))
            {
                // Get the base type name without Awaitable wrapper for combining
                var baseName = IsInterfaceHandleType(memberRef) && TryMapInterfaceInputTypeToTypeScript(memberRef) is { } expanded
                    ? expanded
                    : MapTypeRefToTypeScript(memberRef);
                nonHandleTypes.Add(baseName);
                handleTypeNames.Add(baseName);
            }

View on GitHub (pinned to 25830f84bd)