microsoft/aspire · error · InvalidOperationException
Resource types ' ' all map to the generated TypeScript name…
Error message
Resource types '{typeId}' all map to the generated TypeScript name '{group.Key}', but they are not a concrete type and its interfaces. What it means
Aspire's TypeScript API generator collapses all resource types that map to the same generated TypeScript name into a single declaration. It allows a concrete type plus its interfaces to share a name, but when two or more type IDs with unrelated type relationships collide on the same generated name, the projector cannot pick a single valid representation and throws this InvalidOperationException at generation time.
Solutions
- Rename one of the colliding resource types so each TypeId produces a distinct TypeScript name
- If one type is genuinely an interface of the other, verify the type hierarchy (implements/inherits) is declared so the projector can treat them as concrete + interfaces
- Check the error message for the exact colliding type IDs and adjust the least-used one
- Ensure no two custom resource builders register the same generated name via different type IDs
Example fix
// before
var builder = appBuilder.AddResource(new MyRedisResource("cache")); // typeId: MyRedis
var builder2 = appBuilder.AddResource(new MyRedisResource2("cache2")); // typeId: my-redis -> same TS name
// after
var builder = appBuilder.AddResource(new MyRedisResource("cache")); // typeId: MyRedis -> MyRedis
var builder2 = appBuilder.AddResource(new MyRedisClusterResource("cache2")); // distinct TS name Defensive patterns
Strategy: validation
Validate before calling
var tsNames = new HashSet<string>(StringComparer.Ordinal);
foreach (var resource in resourceTypes)
{
if (!tsNames.Add(GenerateTypeScriptName(resource.TypeId)))
throw new InvalidOperationException($"Type '{resource.TypeId}' collides on generated TypeScript name with an unrelated type.");
} Prevention
- Give each custom resource type a unique, case-distinct TypeId
- After renaming any resource type, regenerate TypeScript and check for collisions immediately
- Keep generated names derived from one canonical casing convention
- Model related types with a real inheritance/interface relationship so the projector can merge them legally
When it happens
Trigger: Defining two or more custom resource types (different TypeIds) whose generated TypeScript names normalize to the same identifier — e.g. types named 'MyRedis' and 'my-redis' or casing/prefix collisions — and building a project that runs the TypeScript code generation.
Common situations: Renaming a resource type so it now collides with an existing one; adding a second custom resource that differs only by case or separators; copying a resource definition from a sample without changing the generated name.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Embedded resource ' ' not found.
- REMOTE_APP_HOST_SOCKET_PATH environment variable not set…
- -32602
- Already connected to
- Argument ' ' passed to capability ' ' contains a circular…
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/880a7b6c11a116c7.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting.CodeGeneration.TypeScript/TypeScriptApiProjector.cs:2846
.ThenBy(builder => builder.BuilderClassName)
.GroupBy(builder => builder.BuilderClassName, StringComparer.Ordinal)
.Select(group =>
{
var candidates = group
.OrderBy(builder => builder.IsInterface)
.ThenBy(builder => builder.TypeId, StringComparer.Ordinal)
.ToList();
var retainedBuilder = candidates[0];
var unrelatedBuilder = candidates
.Skip(1)
.FirstOrDefault(candidate => !IsBuilderAlias(retainedBuilder, candidate));
if (unrelatedBuilder is not null)
{
var collidingTypeIds = candidates
.Select(candidate => candidate.TypeId)
.Order(StringComparer.Ordinal);
throw new InvalidOperationException(
$"Resource types {string.Join(", ", collidingTypeIds.Select(typeId => $"'{typeId}'"))} " +
$"all map to the generated TypeScript name '{group.Key}', but they are not a concrete type and its interfaces.");
}
return retainedBuilder;
})
.ToList();
}
private static void SortOptionsInterfaceCollisionsByCapabilityIdentity(List<AtsCapabilityInfo> capabilities)
{
// Reorder only colliding option-interface slots. Sorting every capability would rewrite
// long-established source order for methods unrelated to the collision.
var collisionGroups = capabilities
.Select((capability, index) => (Capability: capability, Index: index))
.Where(entry =>
{
var (_, optionalParameters) = SeparateParameters(entry.Capability.Parameters);View on GitHub (pinned to 25830f84bd)