dotnet/efcore · error · InvalidOperationException
The entity type ' ' has property ' ' configured as a…
Error message
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'. What it means
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.
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.
Example fix
// before
modelBuilder.Entity<Item>()
.Property(i => i.Version)
.IsConcurrencyToken()
.HasConversion<byte[]>();
// after
modelBuilder.Entity<Item>()
.Property("ETag") // shadow or string property
.IsETagConcurrency(); Defensive patterns
Strategy: validation
Validate before calling
// Verify every concurrency token resolves to _etag.
foreach (var et in modelBuilder.Model.GetEntityTypes())
foreach (var p in et.GetProperties().Where(p => p.IsConcurrencyToken))
if (p.GetJsonPropertyName() != "_etag")
throw new InvalidOperationException($"{et.DisplayName()}.{p.Name} is a concurrency token but not mapped to _etag; use IsETagConcurrency()."); Prevention
- 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.
When it happens
Trigger: 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'.
Common situations: 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.
Related errors
- The type of the etag property
- A full-text index is defined for
- A full-text index on
- A partition key is defined on entity type
- A vector index is defined for
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/e1d8776af1480bea.
Report an issue: GitHub.
Appendix: source
Thrown at src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs:741
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
protected virtual void ValidateConcurrencyToken(
IProperty property,
ITypeBase structuralType,
IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)
{
if (property.IsConcurrencyToken)
{
var storeName = property.GetJsonPropertyName();
if (storeName != "_etag")
{
throw new InvalidOperationException(CosmosStrings.NonETagConcurrencyToken(structuralType.DisplayName(), storeName));
}
var etagType = property.GetTypeMapping().Converter?.ProviderClrType ?? property.ClrType;
if (etagType != typeof(string))
{
throw new InvalidOperationException(
CosmosStrings.ETagNonStringStoreType(property.Name, structuralType.DisplayName(), etagType.ShortDisplayName()));
}
}
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
protected override void ValidateTrigger(View on GitHub (pinned to 3a2006ef56)