HotChocolate code-first triple-nested array throws SchemaException
Product
Hot Chocolate
Version
15.1.10
Link to minimal reproduction
https://github.com/denys-vynohradov-saltykov/hot-chocolate-nested-list-error-poc
Steps to reproduce
- Create web project with package references
HotChocolate.AspNetCoreandHotChocolate.Types.Analyzers - Query.cs:
[QueryType]
public class Query
{
[GraphQLName("coordinates")]
[GraphQLType<NonNullType<ListType<NonNullType<ListType<NonNullType<ListType<FloatType>>>>>>>]
public double[][][] GetCoordinates()
{
return
[
[
[
10d,
20d
]
]
];
}
}
- Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.AddGraphQL()
.InitializeOnStartup()
.AddPoCTypes();
var app = builder.Build();
app.MapGraphQL("/graphql", schemaName: null);
app.MapGraphQLSchema("/graphql/schema");
app.Run();
- Run project
What is expected?
- Project started successfully
- when you navigate into
/graphql/-> schema -> SDL you should see schema
type Query {
coordinates: [[[Float!]!]!]!
}
What is actually happening?
Exception is thrown
HotChocolate.SchemaException: For more details look at the `Errors` property.
1. Unable to resolve type reference `ListType<ListType<ListType<FloatType>!>!>!`. (HotChocolate.Types.ObjectType)
at HotChocolate.Configuration.RegisteredType.GetType[T](TypeReference typeRef)
at HotChocolate.Types.OutputFieldBase.OnCompleteField(ITypeCompletionContext context, ITypeSystemMember declaringMember, OutputFieldDefinitionBase definition)
at HotChocolate.Types.OutputFieldBase.OnCompleteField(ITypeCompletionContext context, ITypeSystemMember declaringMember, FieldDefinitionBase definition)
at HotChocolate.Types.FieldBase.CompleteField(ITypeCompletionContext context, ITypeSystemMember declaringMember)
at HotChocolate.Types.FieldBase.HotChocolate.Types.Helpers.IFieldCompletion.CompleteField(ITypeCompletionContext context, ITypeSystemMember declaringMember)
at HotChocolate.Internal.FieldInitHelper.CompleteFieldsInternal[TField](ITypeCompletionContext context, ITypeSystemMember declaringMember, TField[] fields)
at HotChocolate.Internal.FieldInitHelper.CompleteFieldsInternal[TFieldDefinition,TField](ITypeCompletionContext context, ITypeSystemMember declaringMember, IEnumerable`1 fieldDefinitions, Func`3 fieldFactory, Int32 fieldCount)
at HotChocolate.Internal.FieldInitHelper.CompleteFields[TFieldDefinition,TField](ITypeCompletionContext context, ITypeSystemMember declaringMember, IReadOnlyList`1 fieldDefs, Func`3 fieldFactory)
at HotChocolate.Types.ObjectType.OnCompleteFields(ITypeCompletionContext context, ObjectTypeDefinition definition)
at HotChocolate.Types.ObjectType.OnCompleteType(ITypeCompletionContext context, ObjectTypeDefinition definition)
at HotChocolate.Types.TypeSystemObjectBase`1.CompleteType(ITypeCompletionContext context)
at HotChocolate.Configuration.TypeInitializer.CompleteType(RegisteredType registeredType)
at HotChocolate.Configuration.TypeInitializer.<CompleteTypes>b__33_0(RegisteredType type)
at HotChocolate.Configuration.TypeInitializer.ProcessTypes(TypeDependencyFulfilled fulfilled, Func`2 action)
at HotChocolate.Configuration.TypeInitializer.CompleteTypes()
at HotChocolate.Configuration.TypeInitializer.Initialize()
at HotChocolate.SchemaBuilder.Setup.InitializeTypes(SchemaBuilder builder, IDescriptorContext context, IReadOnlyList`1 types)
at HotChocolate.SchemaBuilder.Setup.Create(SchemaBuilder builder, LazySchema lazySchema, IDescriptorContext context)
at HotChocolate.SchemaBuilder.Create(IDescriptorContext context)
at HotChocolate.SchemaBuilder.HotChocolate.ISchemaBuilder.Create(IDescriptorContext context)
at HotChocolate.Execution.RequestExecutorResolver.CreateSchemaAsync(ConfigurationContext context, RequestExecutorSetup setup, RequestExecutorOptions executorOptions, IServiceProvider schemaServices, TypeModuleChangeMonitor typeModuleChangeMonitor, CancellationToken cancellationToken)
at HotChocolate.Execution.RequestExecutorResolver.CreateSchemaServicesAsync(ConfigurationContext context, RequestExecutorSetup setup, TypeModuleChangeMonitor typeModuleChangeMonitor, CancellationToken cancellationToken)
at HotChocolate.Execution.RequestExecutorResolver.CreateRequestExecutorAsync(String schemaName, Boolean isInitialCreation, CancellationToken cancellationToken)
at HotChocolate.Execution.RequestExecutorResolver.GetRequestExecutorAsync(String schemaName, CancellationToken cancellationToken)
at HotChocolate.Execution.RequestExecutorResolver.<WarmupAsync>g__WarmupSchemaAsync|40_0(String schemaName, CancellationToken cancellationToken)
at HotChocolate.Execution.RequestExecutorResolver.WarmupAsync(CancellationToken cancellationToken)
at HotChocolate.AspNetCore.Warmup.RequestExecutorWarmupService.StartAsync(CancellationToken cancellationToken)
at Microsoft.Extensions.Hosting.Internal.Host.<StartAsync>b__14_1(IHostedService service, CancellationToken token)
at Microsoft.Extensions.Hosting.Internal.Host.ForeachService[T](IEnumerable`1 services, CancellationToken token, Boolean concurrent, Boolean abortOnFirstException, List`1 exceptions, Func`3 operation)
at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken)
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host)
at Program.<Main>$(String[] args)
Relevant log output
Additional context
There is a hardcoded limit to how many nested lists HotChocolate allows https://github.com/ChilliCream/graphql-platform/blob/ce7a74a2edb7f0a66dd6cd84dedd39492c3a8782/src/HotChocolate/Core/src/Types/Internal/TypeInfo.cs#L185
Why do we need triple-nested arrays in the schema:
- to introduce subset of GeoJSON standard into the schema. GeoJSON Polygon coordinates is an array of array of array. https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.6
- HotChocolate.Data.Spatial is overkill since we do not need most of the types provided in the package
Known workaround is to introduce custom scalar that represents float array
"""
Array of floating point numbers
For example: [3.14, 2.7]
"""
scalar Position
type Query {
coordinates: [[Position!]!]!
}
We actually have native support for GeoJSON
https://github.com/ChilliCream/graphql-platform/tree/main/src/HotChocolate/Spatial
https://github.com/ChilliCream/graphql-platform/tree/main/src/HotChocolate/Spatial/src/Types
@michaelstaib thank you for the reply.
As I said, HotChocolate.Data.Spatial package is overkill for my use case. There are dozens of types in the package that will never be used.
I just want to highlight the bigger issue here. HotChocolate has hardcoded limit to how many nested lists are allowed. There is no such limit in the GraphQL specification.
This was a deliberate design decision: we currently support a nesting depth of two for lists. This keeps our implementation consistent with the examples provided in the GraphQL specification. While it would be possible to relax this restriction, we haven’t yet seen strong use cases that would justify going deeper.
When we looked into GeoJSON, for example, we found that its values are essentially scalar-like, so more than two levels of nesting weren’t necessary. Allowing deeper nesting would add performance costs and complexity without clear benefits. That said, the specification does not forbid deeper nesting, so it would be technically valid to extend it. We simply chose to align with the coercion examples in the spec, which only demonstrate lists and simple nested lists.
For GeoJSON specifically: you only need to register the scalar you actually use—nothing else is initialized unnecessarily. If your spatial data use case is already covered by the provided implementation, you should be good to go.
That said, if you have a concrete use case where deeper nesting would provide clear value, we’d love to hear more about it. Understanding real-world needs is always helpful for us in shaping the direction of the library.