{"record":{"id":"0d08a3f83ac05223","repo":"microsoft/aspire","slug":"union-input-types-must-define-at-least-one-member-type","errorCode":null,"errorMessage":"Union input types must define at least one member type.","messagePattern":"Union input types must define at least one member type\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.CodeGeneration.TypeScript/TypeScriptApiProjector.cs","lineNumber":1898,"sourceCode":"\n        if (typeRef?.TypeId == InteractionInputCollectionTypeId)\n        {\n            return $\"Awaitable<{GetInteractionInputCollectionClassName()}>\";\n        }\n\n        if (IsCancellationTokenType(typeRef))\n        {\n            return $\"AbortSignal | {GetCancellationTokenInterfaceName()}\";\n        }\n\n        return MapTypeRefToTypeScript(typeRef);\n    }\n\n    internal string MapInputUnionTypeToTypeScript(AtsTypeRef typeRef)\n    {\n        if (typeRef.UnionTypes == null || typeRef.UnionTypes.Count == 0)\n        {\n            throw new InvalidOperationException(\"Union input types must define at least one member type.\");\n        }\n\n        // Build union structurally: each member is mapped individually.\n        // Handle types become Awaitable<T>, non-handle types pass through as-is.\n        var nonHandleTypes = new List<string>();\n        var handleTypeNames = new List<string>();\n\n        foreach (var memberRef in typeRef.UnionTypes)\n        {\n            if (IsWidenedHandleType(memberRef))\n            {\n                // Get the base type name without Awaitable wrapper for combining\n                var baseName = IsInterfaceHandleType(memberRef) && TryMapInterfaceInputTypeToTypeScript(memberRef) is { } expanded\n                    ? expanded\n                    : MapTypeRefToTypeScript(memberRef);\n                nonHandleTypes.Add(baseName);\n                handleTypeNames.Add(baseName);\n            }","sourceCodeStart":1880,"sourceCodeEnd":1916,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.CodeGeneration.TypeScript/TypeScriptApiProjector.cs#L1880-L1916","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Populate typeRef.UnionTypes with at least one member AtsTypeRef before projecting.","Fix the analyzer/builder that produced the empty union (usually means it failed to resolve member types).","If the type is genuinely not a union, construct a plain type ref instead of an empty union.","Log the offending typeId to locate where the incomplete AtsTypeRef was created."],"exampleFix":"// before\nvar typeRef = new AtsTypeRef { UnionTypes = new List<AtsTypeRef>() }; // empty union\nvar ts = projector.MapInputUnionTypeToTypeScript(typeRef); // throws\n\n// after\ntypeRef.UnionTypes.Add(new AtsTypeRef { /* string member */ });\ntypeRef.UnionTypes.Add(new AtsTypeRef { /* number member */ });\nvar ts = projector.MapInputUnionTypeToTypeScript(typeRef); // \"string | number\"","handlingStrategy":"validation","validationCode":"if (typeRef.UnionTypes is not { Count: > 0 }) {\n    throw new InvalidOperationException(\"populate UnionTypes with at least one member before mapping\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    var ts = MapInputUnionTypeToTypeScript(typeRef);\n} catch (InvalidOperationException ex) when (ex.Message.Contains(\"at least one member\")) {\n    // fix the builder that produced the empty union and retry\n    throw;\n}","preventionTips":["Always add at least one member when constructing a union type ref.","Fix resolvers that silently emit empty unions when member types fail to resolve.","Use plain type refs for non-union types instead of empty unions.","Add model validation after building AtsTypeRef instances."],"tags":["union","code-generation","validation","csharp"],"backgroundTag":"empty-required-field","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}