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

  1. Replace IsConcurrencyToken() with the Cosmos-specific IsETagConcurrency() helper, which maps the property to '_etag' for you.
  2. If the property must keep a custom JSON name for other reasons, stop marking it a concurrency token and handle concurrency a different way.
  3. 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

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


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)