Summary
The Response → Result model naming change in commit d5ef03c (#11999) exposes a pre-existing type-identity mismatch in the generator's keep-set handling.
An otherwise-unreferenced service model named ErrorResponse is renamed to ErrorResult and then incorrectly matches the keep entry intended for the internal generic helper ErrorResult<T>. Consequently, error models that were previously pruned are emitted, often as new public API.
Observed impact
Azure/azure-sdk-for-net#63407 regenerates SDKs with @azure-typespec/http-client-csharp 1.0.0-alpha.20260925.2, which depends on @typespec/http-client-csharp 1.0.0-alpha.20260924.3.
The PR introduces 22 new non-generic ErrorResult models: 19 public and 3 internal, across libraries including Content Safety, Content Understanding, Communication, Language, and others. Public additions also include model-factory methods and serialization APIs.
For example, Content Safety now contains both:
// Existing generator-owned helper in Generated/Internal/ErrorResult.cs
internal partial class ErrorResult<T> : Response<T>
// Newly retained service model in Generated/Models/ErrorResult.cs
public partial class ErrorResult
{
public ResponseError Error { get; }
public string ErrorCode { get; }
}
The non-generic Content Safety model has no client-operation references; its generated references are its serialization implementation, model factory, and model-reader/writer context.
Separately, Anomaly Detector and Defender EASM each rename an already-emitted ErrorResponse model to ErrorResult. Those two renames are distinct from the 22 newly retained models described here.
Root-cause trace
ModelProvider.NormalizeTypeName changes the model suffix from Response to Result, subject to its exact-name/customization/last-contract preservation rules.
ErrorResultDefinition defines the generic helper. Its InternalHelperProvider base constructor calls AddTypeToKeep(this, isRoot: false).
CodeModelGenerator.MaterializeKeepSet stores provider.Type.FullyQualifiedName. That property omits generic arity.
- Reference-map provider identities, however, include generic arity. The stored keep key therefore exactly matches the non-generic model, rather than identifying only the intended generic helper.
AddKeptNonRootNames and RemoveKeptNonRootNames preserve the matching model against removal and, where applicable, internalization.
For Content Safety, the identities are:
Intended helper metadata identity: Azure.AI.ContentSafety.ErrorResult`1
Stored helper keep key: Azure.AI.ContentSafety.ErrorResult
New service model identity: Azure.AI.ContentSafety.ErrorResult
This repeats independently in each affected library's namespace. The suffix rename exposes the collision; it does not introduce a new service error contract.
Reproduction
Use the Content Safety package and pinned spec from Azure/azure-sdk-for-net#63407:
- Compare generation using
@azure-typespec/http-client-csharp 1.0.0-alpha.20260924.2 and 1.0.0-alpha.20260925.2.
- Inspect
sdk/contentsafety/Azure.AI.ContentSafety/src/Generated/Models/ErrorResult.cs and the exported API.
- Observe that the newer generation emits the previously-pruned error wrapper and adds
ContentSafetyModelFactory.ErrorResult(...), while the unrelated generic helper already existed before the update.
The diagnosis above is based on the regeneration diff and the generator source at the linked commit; a standalone generator regression test has not yet been added.
Expected behavior / suggested fixes
There are two independent requirements to address:
1. Exempt error response models from automatic Response-to-Result renaming
Error response models should retain their names rather than having the automatic suffix normalization rename them from ErrorResponse to ErrorResult. This applies independently of whether a model is pruned or retained, and should not depend on a previous contract or a hand-written customization being present.
The exclusion should apply to models identified as error responses, not just a special case for the literal name ErrorResponse. It should not disable the intended suffix normalization for ordinary non-error response models. Explicit user naming customizations should continue to be honored.
Fixing the keep-key collision alone would stop unintended model emission, but would not address undesired renaming of error response models that are legitimately retained. Naming and reachability need to be handled separately.
2. Fix the generic/non-generic keep-key collision
Use consistent, generic-arity-aware identities when materializing provider keep registrations and matching them against reference-map nodes. Preserving ErrorResult<T> must not preserve ErrorResult merely because their namespace and simple name match.
Exempting error response models from renaming avoids this particular trigger, but is not a substitute for fixing the underlying identity mismatch: a non-generic model already named ErrorResult can still collide with the helper.
Suggested regression coverage
- An error response model named
ErrorResponse retains that name, including when no previous contract or customization exists.
- A differently named error response model ending in
Response also retains its name.
- A legitimately referenced error response model is retained without automatic suffix renaming; an unreferenced error response model remains eligible for normal pruning.
- Ordinary non-error response models continue to receive the intended
Response-to-Result normalization, subject to existing naming-preservation rules.
- Explicit naming customizations remain honored.
- An unreferenced non-generic model already named
ErrorResult is still pruned when the internal ErrorResult<T> helper is present.
- The generic helper remains available where required and remains internal.
- No public non-generic
ErrorResult, model-factory method, or context registration is introduced solely by the helper's keep registration.
- A genuinely referenced non-generic
ErrorResult is retained normally.
- Generic and non-generic providers sharing a namespace and simple name cannot preserve one another accidentally.
Both changes should be made in the generator rather than adding per-library naming customizations or suppressions in the regenerated SDKs.
Summary
The
Response→Resultmodel naming change in commit d5ef03c (#11999) exposes a pre-existing type-identity mismatch in the generator's keep-set handling.An otherwise-unreferenced service model named
ErrorResponseis renamed toErrorResultand then incorrectly matches the keep entry intended for the internal generic helperErrorResult<T>. Consequently, error models that were previously pruned are emitted, often as new public API.Observed impact
Azure/azure-sdk-for-net#63407 regenerates SDKs with
@azure-typespec/http-client-csharp1.0.0-alpha.20260925.2, which depends on@typespec/http-client-csharp1.0.0-alpha.20260924.3.The PR introduces 22 new non-generic
ErrorResultmodels: 19 public and 3 internal, across libraries including Content Safety, Content Understanding, Communication, Language, and others. Public additions also include model-factory methods and serialization APIs.For example, Content Safety now contains both:
The non-generic Content Safety model has no client-operation references; its generated references are its serialization implementation, model factory, and model-reader/writer context.
Separately, Anomaly Detector and Defender EASM each rename an already-emitted
ErrorResponsemodel toErrorResult. Those two renames are distinct from the 22 newly retained models described here.Root-cause trace
ModelProvider.NormalizeTypeNamechanges the model suffix fromResponsetoResult, subject to its exact-name/customization/last-contract preservation rules.ErrorResultDefinitiondefines the generic helper. ItsInternalHelperProviderbase constructor callsAddTypeToKeep(this, isRoot: false).CodeModelGenerator.MaterializeKeepSetstoresprovider.Type.FullyQualifiedName. That property omits generic arity.AddKeptNonRootNamesandRemoveKeptNonRootNamespreserve the matching model against removal and, where applicable, internalization.For Content Safety, the identities are:
This repeats independently in each affected library's namespace. The suffix rename exposes the collision; it does not introduce a new service error contract.
Reproduction
Use the Content Safety package and pinned spec from Azure/azure-sdk-for-net#63407:
@azure-typespec/http-client-csharp1.0.0-alpha.20260924.2 and 1.0.0-alpha.20260925.2.sdk/contentsafety/Azure.AI.ContentSafety/src/Generated/Models/ErrorResult.csand the exported API.ContentSafetyModelFactory.ErrorResult(...), while the unrelated generic helper already existed before the update.The diagnosis above is based on the regeneration diff and the generator source at the linked commit; a standalone generator regression test has not yet been added.
Expected behavior / suggested fixes
There are two independent requirements to address:
1. Exempt error response models from automatic Response-to-Result renaming
Error response models should retain their names rather than having the automatic suffix normalization rename them from
ErrorResponsetoErrorResult. This applies independently of whether a model is pruned or retained, and should not depend on a previous contract or a hand-written customization being present.The exclusion should apply to models identified as error responses, not just a special case for the literal name
ErrorResponse. It should not disable the intended suffix normalization for ordinary non-error response models. Explicit user naming customizations should continue to be honored.Fixing the keep-key collision alone would stop unintended model emission, but would not address undesired renaming of error response models that are legitimately retained. Naming and reachability need to be handled separately.
2. Fix the generic/non-generic keep-key collision
Use consistent, generic-arity-aware identities when materializing provider keep registrations and matching them against reference-map nodes. Preserving
ErrorResult<T>must not preserveErrorResultmerely because their namespace and simple name match.Exempting error response models from renaming avoids this particular trigger, but is not a substitute for fixing the underlying identity mismatch: a non-generic model already named
ErrorResultcan still collide with the helper.Suggested regression coverage
ErrorResponseretains that name, including when no previous contract or customization exists.Responsealso retains its name.Response-to-Resultnormalization, subject to existing naming-preservation rules.ErrorResultis still pruned when the internalErrorResult<T>helper is present.ErrorResult, model-factory method, or context registration is introduced solely by the helper's keep registration.ErrorResultis retained normally.Both changes should be made in the generator rather than adding per-library naming customizations or suppressions in the regenerated SDKs.