{"record":{"id":"e2a2120a8c0d0fe9","repo":"microsoft/aspire","slug":"cannot-support-a-generic-interface-that-doesn-t-have-exactly","errorCode":null,"errorMessage":"Cannot support a generic interface that doesn't have exactly 1 argument.","messagePattern":"Cannot support a generic interface that doesn't have exactly 1 argument\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.CodeGeneration.Python/AtsPythonCodeGenerator.cs","lineNumber":1556,"sourceCode":"            if (implementedInterfaces is { Count: > 0 })\n            {\n                // Remove interfaces that are already implemented by another interface in the list\n                var transitivelyImplemented = new HashSet<string>(\n                    implementedInterfaces\n                        .SelectMany(i => i.ImplementedInterfaces ?? [])\n                        .Select(i => i.TypeId),\n                    StringComparer.Ordinal);\n                implementedInterfaces = implementedInterfaces\n                    .Where(i => !transitivelyImplemented.Contains(i.TypeId))\n                    .ToList();\n\n                foreach (var i in implementedInterfaces)\n                {\n                    if (i.ClrType!.IsGenericType)\n                    {\n                        if (i.ClrType.GenericTypeArguments == null || i.ClrType.GenericTypeArguments.Length != 1)\n                        {\n                            throw new InvalidOperationException(\"Cannot support a generic interface that doesn't have exactly 1 argument.\");\n                        }\n                        var genericSubType = i.ClrType.GenericTypeArguments[0];\n                        baseClass += $\", {DeriveClassName(i)}[\\\"{DeriveClassName(genericSubType)}\\\"]\";\n                    }\n                    else\n                    {\n                        baseClass += \", \" + DeriveClassName(i);\n                    }\n                }\n            }\n        \n            sb.AppendLine(CultureInfo.InvariantCulture, $\"class {builder.BuilderClassName}({baseClass}):\");\n            sb.AppendLine(CultureInfo.InvariantCulture, $\"    \\\"\\\"\\\"{builder.BuilderClassName} resource.\\\"\\\"\\\"\");\n            sb.AppendLine();\n            sb.AppendLine(\"    def __repr__(self) -> str:\");\n            sb.AppendLine(CultureInfo.InvariantCulture, $\"        return \\\"{builder.BuilderClassName}(handle={{self._handle.handle_id}})\\\"\");\n            sb.AppendLine();\n            sbOptions.AppendLine(CultureInfo.InvariantCulture, $\"class {builder.BuilderClassName}Kwargs({optionsBaseClass}, total=False):\");","sourceCodeStart":1538,"sourceCodeEnd":1574,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.CodeGeneration.Python/AtsPythonCodeGenerator.cs#L1538-L1574","documentation":"When generating Python classes, the code generator maps C# interfaces that the resource type implements to Python generic base classes, which requires each generic interface to have exactly one type argument (Python generics like Base[\"T\"]). If a resource type implements a generic interface with zero or multiple type arguments, the generator cannot express it and throws this InvalidOperationException.","triggerScenarios":"Running the Python code generator (GenerateDistributedApplication) against a resource model where an ATS-registered type implements a generic interface whose GenericTypeArguments array is null or has a length other than 1.","commonSituations":"Registering a custom resource type that implements something like IDictionary<string, string> or a generic callback interface with 2 type parameters, then generating Python stubs for the app model; adding a new capability interface to the ATS surface without updating the generator.","solutions":["Change the resource/capability type to implement a single-generic-argument interface (e.g. wrap a two-parameter generic in a dedicated single-parameter interface) before generating Python code.","If you own the ATS registration, register a non-generic or singly-generic view type for the resource instead of the raw multi-arg generic interface.","Check which interface the generator iterates (the message follows the implementedInterfaces loop) and remove that interface from the type's implemented surface if it is not needed for Python.","If this is a framework type you cannot change, file an issue asking the generator to add a mapping/skip rule for that interface."],"exampleFix":"// before\nclass MyStore : IResource, IDictionary<string, string> { ... }\n\n// after\nclass MyStore : IResource, IEnumerable<KeyValuePair<string, string>> { ... }  // single generic argument","handlingStrategy":"validation","validationCode":"// before generating, verify each implemented generic interface has exactly 1 type arg\nbool generatable = type.GetInterfaces()\n  .Where(i => i.IsGenericType)\n  .All(i => i.GetGenericArguments().Length == 1);","typeGuard":"function isSingleGenericInterface(t) { return t.genericTypeArguments != null && t.genericTypeArguments.length === 1; }","tryCatchPattern":"try {\n  await generator.GenerateDistributedApplication(context);\n} catch (InvalidOperationException ex) when (ex.Message.Contains(\"generic interface\")) {\n  // identify the offending interface and adjust the resource model\n}","preventionTips":["Design resource/capability types with single-parameter generic interfaces only.","Wrap multi-generic interfaces in dedicated single-generic views before registering them in ATS.","Test code generation whenever you add a new interface to a resource type."],"tags":["python","code-generation","generics","unsupported"],"backgroundTag":"unsupported-operation","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"}