diff --git a/src/HotChocolate/Core/src/Types.Mutations/MutationConventionTypeInterceptor.cs b/src/HotChocolate/Core/src/Types.Mutations/MutationConventionTypeInterceptor.cs index a4b10f5d95d..4ae4dd9bc05 100644 --- a/src/HotChocolate/Core/src/Types.Mutations/MutationConventionTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Types.Mutations/MutationConventionTypeInterceptor.cs @@ -713,13 +713,22 @@ private void EnsureTypeReferenceRegistered( return; } + // Unwrap arrays and collection-interface wrappers (IList, ICollection, + // IEnumerable, IReadOnlyList, ...). The wrapper itself is not a named + // schema type, and CLR collection interfaces carry TypeAttributes.Interface + // which would otherwise misclassify them as a GraphQL interface here. + while (runtimeTypeReference.Type is { IsArrayOrList: true, ElementType: { } element }) + { + runtimeTypeReference = runtimeTypeReference.WithType(element); + } + if (_typeRegistry.TryGetTypeRef(runtimeTypeReference, out var existingTypeReference) && _typeRegistry.IsRegistered(existingTypeReference)) { return; } - if (!_context.TryInferSchemaType(typeReference, out var schemaTypeReferences)) + if (!_context.TryInferSchemaType(runtimeTypeReference, out var schemaTypeReferences)) { return; } diff --git a/src/HotChocolate/Core/test/Types.Mutations.Tests/AnnotationBasedMutations.cs b/src/HotChocolate/Core/test/Types.Mutations.Tests/AnnotationBasedMutations.cs index a5e6c1ea682..8606b232d7b 100644 --- a/src/HotChocolate/Core/test/Types.Mutations.Tests/AnnotationBasedMutations.cs +++ b/src/HotChocolate/Core/test/Types.Mutations.Tests/AnnotationBasedMutations.cs @@ -747,6 +747,24 @@ public async Task Union_Result_2_Error_Name_Collision() exception.Message.MatchSnapshot(); } + [Fact] + public async Task Error_Type_With_IList_Property_Should_Be_Treated_As_List() + { + // arrange & act + var schema = + await new ServiceCollection() + .AddGraphQL() + .AddMutationType() + .AddMutationConventions() + .ModifyOptions(o => o.StrictValidation = false) + .BuildSchemaAsync(); + + // assert + var errorType = schema.Types.GetType("ErrorWithCodes"); + Assert.Equal("[Int!]!", errorType.Fields["codes"].Type.ToString()); + Assert.False(schema.Types.TryGetType("IListOfInt32", out _)); + } + [Fact] public async Task Union_Result_3() { @@ -1746,6 +1764,17 @@ public FooError(string message) public string Message { get; } } + public class MutationWithIListError + { + public FieldResult DoSomething(int[] codes) + => new ErrorWithCodes(codes); + } + + public record ErrorWithCodes(IList Codes) + { + public string Message => $"Codes {string.Join(", ", Codes)} not found."; + } + public class MutationWithPayloadOverride { [Error]