{"record":{"id":"5fa04bcdfec6846a","repo":"dotnet/efcore","slug":"the-index-indexproperties-on-the-entity-type-e","errorCode":null,"errorMessage":"The index {indexProperties} on the entity type '{entityType}' cannot be configured because its property '{property}' traverses a complex collection but is not mapped to a JSON column.","messagePattern":"The index (.+?) on the entity type '(.+?)' cannot be configured because its property '(.+?)' traverses a complex collection but is not mapped to a JSON column\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs","lineNumber":2720,"sourceCode":"        {\n            var property = index.Properties[i];\n\n            // Only properties mapped inside a JSON container matter here. Mixed JSON / non-JSON indexes\n            // are rejected earlier by ValidateIndexPropertyMapping.\n            var container = property.DeclaringType is IReadOnlyComplexType complexType && complexType.IsMappedToJson()\n                ? complexType.GetContainerColumnName()\n                : property is IReadOnlyComplexProperty complexProperty && complexProperty.ComplexType.IsMappedToJson()\n                    ? complexProperty.ComplexType.GetContainerColumnName()\n                    : null;\n\n            if (container is null)\n            {\n                // Not a JSON-contained property. If this position carries a non-null collection-indices\n                // entry (i.e., the path traverses a complex collection but doesn't end in JSON), the\n                // index identity points at a JSON path that has no JSON container — that's invalid.\n                if (index.CollectionIndices?[i] is not null)\n                {\n                    throw new InvalidOperationException(\n                        RelationalStrings.JsonPathIndexPropertyMissingJsonColumn(\n                            index.Properties.Format(),\n                            index.DeclaringEntityType.DisplayName(),\n                            property.Name));\n                }\n\n                continue;\n            }\n\n            if (firstContainer is null)\n            {\n                firstContainer = container;\n            }\n            else if (!string.Equals(firstContainer, container, StringComparison.Ordinal))\n            {\n                throw new InvalidOperationException(\n                    RelationalStrings.JsonPathIndexPropertiesInDifferentJsonColumns(\n                        index.Properties.Format(),","sourceCodeStart":2702,"sourceCodeEnd":2738,"githubUrl":"https://github.com/dotnet/efcore/blob/3a2006ef569de08368d59db5e1468aa8f407e4f8/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs#L2702-L2738","documentation":"A JSON-path index is one whose property path traverses a complex collection (so index.CollectionIndices is populated). Each leaf of such an index must live inside a JSON column so the path can be expressed in the document. If a position has a non-null CollectionIndices entry but the property at that position has no JSON container column, the index identity points into a JSON path that has no JSON document, which is invalid.","triggerScenarios":"Defining a composite index whose properties include one reached through a complex collection that is NOT mapped to JSON (e.g. complex collection mapped to separate tables/columns), while CollectionIndices is set for that position. Mixing JSON-path index semantics with a non-JSON complex collection.","commonSituations":"Upgrading to an EF version that introduced JSON-path indexes and a previously-tolerated index now traverses a non-JSON collection; configuring .HasIndex over a path into a complex collection mapped via ToTable splitting; copying JSON index config onto a non-JSON entity.","solutions":["Map the complex type that contains the property to JSON (.ToJsonb() / .ToJson()) so the leaf gets a container column.","Remove the index, or stop traversing the complex collection in it — index scalar properties directly on the entity instead.","Clear CollectionIndices on that index position if you did not intend a JSON-path index."],"exampleFix":"// before — index traverses a complex collection not mapped to JSON -> [444]\nmodelBuilder.Entity<Cart>()\n    .OwnsMany(c => c.Items, i =>\n    {\n        i.HasIndex(x => x.Sku); // Items is a complex collection, not JSON\n    });\n\n// after — map the collection to JSON before indexing into it\nmodelBuilder.Entity<Cart>()\n    .OwnsMany(c => c.Items, i =>\n    {\n        i.ToJson(\"items\");\n        i.HasIndex(x => x.Sku);\n    });","handlingStrategy":"validation","validationCode":"// Before defining a JSON-path index, ensure every traversed complex collection is JSON-mapped.\nstatic bool IndexLeavesAreJsonContained(IIndex index)\n    => index.Properties.All(p =>\n        (p.DeclaringType is IReadOnlyComplexType ct && ct.IsMappedToJson())\n        || (p is IReadOnlyComplexProperty cp && cp.ComplexType.IsMappedToJson()));","typeGuard":"static bool IsJsonPathIndexValid(IIndex index)\n    => index.CollectionIndices is null\n       || index.Properties.All(p =>\n            (p.DeclaringType is IReadOnlyComplexType ct && ct.IsMappedToJson())\n            || (p is IReadOnlyComplexProperty cp && cp.ComplexType.IsMappedToJson()));","tryCatchPattern":null,"preventionTips":["Map complex collections to JSON before indexing into them.","Avoid indexing through non-JSON complex collections.","Add a model-validation test that exercises indexes on complex types."],"tags":["json","complex-types","indexes","model-validation"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}