dotnet/efcore · error · InvalidOperationException

The default time to live was configured to

Error message

The default time to live was configured to '{ttl1}' on '{entityType1}', but on '{entityType2}' it was configured to '{ttl2}'. All entity types mapped to the same container '{container}' must be configured with the same default time to live.

What it means

Thrown when the default time-to-live (DefaultTimeToLive) differs across entity types mapped to the same Cosmos container. Default TTL is a container-level Cosmos setting that auto-expires documents, so every type sharing the container must agree. The validator keeps the first non-null value and throws on the next mismatch.

Solutions

  1. Choose the single TTL the container must use and apply it once on the document-root entity.
  2. Search for every HasDefaultTimeToLive call against entities mapped to the named container and delete the conflicting ones.
  3. Verify the units: HasDefaultTimeToLive expects a TimeSpan; double-check you did not pass seconds where hours were intended.

Example fix

// before
modelBuilder.Entity<User>().ToContainer("Users").HasDefaultTimeToLive(TimeSpan.FromDays(30));
modelBuilder.Entity<Guest>().ToContainer("Users").HasDefaultTimeToLive(TimeSpan.FromDays(7));

// after
modelBuilder.Entity<User>().ToContainer("Users").HasDefaultTimeToLive(TimeSpan.FromDays(30));
modelBuilder.Entity<Guest>().ToContainer("Users");
Defensive patterns

Strategy: validation

Validate before calling

foreach (var g in dbContext.Model.GetEntityTypes().GroupBy(e => e.GetContainer()))
{
    var ttls = g.Select(e => e.GetDefaultTimeToLive()).Where(t => t != null).Distinct().ToList();
    if (ttls.Count > 1) throw new InvalidOperationException($"Container {g.Key} has conflicting default TTLs");
}

Prevention

When it happens

Trigger: One entity type calls HasDefaultTimeToLive(TimeSpan) with one value and another type in the same container calls it with a different value (or with null while the first set a non-null value). Fires during model finalization/validation.

Common situations: Setting per-type TTLs before realizing the types share a container; migrating from per-entity TTL design to shared-container (TPT-like) design without scrubbing old TTL calls; mixing TimeSpan.Zero (no TTL) with non-zero values across the hierarchy.

Related errors


AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11). Data as JSON: /api/errors/c17cd3e9c4091f17. Report an issue: GitHub.

Appendix: source

Thrown at src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs:266

                            analyticalTtl,
                            conflictingEntityType.DisplayName(),
                            entityType.DisplayName(),
                            currentAnalyticalTtl,
                            container));
                }
            }

            var currentDefaultTtl = entityType.GetDefaultTimeToLive();
            if (currentDefaultTtl != null)
            {
                if (defaultTtl == null)
                {
                    defaultTtl = currentDefaultTtl;
                }
                else if (defaultTtl != currentDefaultTtl)
                {
                    var conflictingEntityType = mappedTypes.First(et => et.GetDefaultTimeToLive() != null);
                    throw new InvalidOperationException(
                        CosmosStrings.DefaultTTLMismatch(
                            defaultTtl,
                            conflictingEntityType.DisplayName(),
                            entityType.DisplayName(),
                            currentDefaultTtl,
                            container));
                }
            }

            var currentThroughput = entityType.GetThroughput();
            if (currentThroughput != null)
            {
                if (throughput == null)
                {
                    throughput = currentThroughput;
                }
                else if ((throughput.AutoscaleMaxThroughput ?? throughput.Throughput)
                         != (currentThroughput.AutoscaleMaxThroughput ?? currentThroughput.Throughput))

View on GitHub (pinned to 3a2006ef56)