{"record":{"id":"e1d8776af1480bea","repo":"dotnet/efcore","slug":"the-entity-type-entitytype-has-property-prop","errorCode":null,"errorMessage":"The entity type '{entityType}' has property '{property}' configured as a concurrency token, but only a property mapped to '_etag' is supported as a concurrency token. Consider using 'PropertyBuilder.IsETagConcurrency'.","messagePattern":"The entity type '(.+?)' has property '(.+?)' configured as a concurrency token, but only a property mapped to '_etag' is supported as a concurrency token\\. Consider using 'PropertyBuilder\\.IsETagConcurrency'\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs","lineNumber":741,"sourceCode":"    }\n\n    /// <summary>\n    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to\n    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in\n    ///     any release. You should only use it directly in your code with extreme caution and knowing that\n    ///     doing so can result in application failures when updating to a new Entity Framework Core release.\n    /// </summary>\n    protected virtual void ValidateConcurrencyToken(\n        IProperty property,\n        ITypeBase structuralType,\n        IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)\n    {\n        if (property.IsConcurrencyToken)\n        {\n            var storeName = property.GetJsonPropertyName();\n            if (storeName != \"_etag\")\n            {\n                throw new InvalidOperationException(CosmosStrings.NonETagConcurrencyToken(structuralType.DisplayName(), storeName));\n            }\n\n            var etagType = property.GetTypeMapping().Converter?.ProviderClrType ?? property.ClrType;\n            if (etagType != typeof(string))\n            {\n                throw new InvalidOperationException(\n                    CosmosStrings.ETagNonStringStoreType(property.Name, structuralType.DisplayName(), etagType.ShortDisplayName()));\n            }\n        }\n    }\n\n    /// <summary>\n    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to\n    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in\n    ///     any release. You should only use it directly in your code with extreme caution and knowing that\n    ///     doing so can result in application failures when updating to a new Entity Framework Core release.\n    /// </summary>\n    protected override void ValidateTrigger(","sourceCodeStart":723,"sourceCodeEnd":759,"githubUrl":"https://github.com/dotnet/efcore/blob/3a2006ef569de08368d59db5e1468aa8f407e4f8/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs#L723-L759","documentation":"Thrown by CosmosModelValidator.ValidateConcurrencyToken when a property is marked IsConcurrencyToken but its JSON property name is not '_etag'. Cosmos DB only supports optimistic concurrency via the system _etag field, so any other concurrency token is invalid.","triggerScenarios":"Calling .IsConcurrencyToken() on a non-etag property (e.g. a RowVersion byte[] pattern ported from SQL Server). The validator compares property.GetJsonPropertyName() to the literal '_etag'.","commonSituations":"Copying concurrency patterns from a relational provider into a Cosmos model; renaming the etag property or mapping it to a different JSON name; using [Timestamp] attributes.","solutions":["Replace IsConcurrencyToken() with the Cosmos-specific IsETagConcurrency() helper, which maps the property to '_etag' for you.","If the property must keep a custom JSON name for other reasons, stop marking it a concurrency token and handle concurrency a different way.","Remove IsConcurrencyToken entirely if you do not need optimistic concurrency on that entity."],"exampleFix":"// before\nmodelBuilder.Entity<Item>()\n    .Property(i => i.Version)\n    .IsConcurrencyToken()\n    .HasConversion<byte[]>();\n\n// after\nmodelBuilder.Entity<Item>()\n    .Property(\"ETag\")              // shadow or string property\n    .IsETagConcurrency();","handlingStrategy":"validation","validationCode":"// Verify every concurrency token resolves to _etag.\nforeach (var et in modelBuilder.Model.GetEntityTypes())\nforeach (var p in et.GetProperties().Where(p => p.IsConcurrencyToken))\n    if (p.GetJsonPropertyName() != \"_etag\")\n        throw new InvalidOperationException($\"{et.DisplayName()}.{p.Name} is a concurrency token but not mapped to _etag; use IsETagConcurrency().\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use IsETagConcurrency() exclusively for Cosmos concurrency; never IsConcurrencyToken() with relational patterns.","Do not port byte[]/RowVersion concurrency fields into Cosmos models.","Add a startup check that asserts all concurrency tokens map to _etag."],"tags":["cosmos","concurrency","etag","model-validation","configuration"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}