{"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/dbf9771522148d61a2467854921bd5dc6f6e6916/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 meaningless and rejected. The message recommends PropertyBuilder.IsETagConcurrency as the correct API.","triggerScenarios":"Calling builder.Property(x => x.Version).IsRowVersion() or IsConcurrencyToken() on a Cosmos entity without mapping that property to the _etag JSON name.","commonSituations":"Porting a relational model that uses a RowVersion/Timestamp column for optimistic concurrency. Using a shared entity type across SQL Server and Cosmos where the relational concurrency column is not an etag on Cosmos.","solutions":["Replace IsConcurrencyToken/IsRowVersion with the Cosmos-specific IsETagConcurrency extension on the property builder.","If you keep a separate property, ensure its JSON property name maps to '_etag' via HasJsonPropertyName(\"_etag\") and that it is the etag concurrency property."],"exampleFix":"// before\nmodelBuilder.Entity<Item>()\n    .Property(i => i.Version)\n    .IsRowVersion();\n\n// after\nmodelBuilder.Entity<Item>()\n    .Property(i => i.ETag)\n    .HasJsonPropertyName(\"_etag\")\n    .IsETagConcurrency();","handlingStrategy":"validation","validationCode":"// Fail fast if a concurrency token is not the etag\nforeach (var et in modelBuilder.Model.GetEntityTypes())\n{\n    foreach (var prop in et.GetProperties())\n    {\n        if (prop.IsConcurrencyToken && prop.GetJsonPropertyName() != \"_etag\")\n        {\n            throw new InvalidOperationException($\"{et.DisplayName()}.{prop.Name} is a concurrency token but not _etag.\");\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["On Cosmos entities, always use the IsETagConcurrency extension instead of IsConcurrencyToken/IsRowVersion.","Keep a single string ETag property per Cosmos entity mapped to '_etag'."],"tags":["cosmos","ef-core","model-validation","concurrency","etag"],"analyzedSha":"dbf9771522148d61a2467854921bd5dc6f6e6916","analyzedAt":"2026-08-06T20:46:03.226Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}