dotnet/efcore · error · InvalidOperationException

The default time to live was configured to '{ttl1}' on '{ent

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 by CosmosModelValidator.ValidateSharedContainerCompatibility when two document-root entity types in the same container declare different default time-to-live values via HasDefaultTimeToLive(). Default TTL is a container-level Cosmos setting, so all entity types sharing a container must specify the same value. Derived types are compared through their root, so this fires only across distinct document roots.

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 dbf9771522)

Solutions

  1. Align every root entity mapped to the container on the same HasDefaultTimeToLive(N) value.
  2. If only one entity should 'own' the container setting, remove HasDefaultTimeToLive from the others.
  3. Drive container-level options from a single configuration method applied to all shared-container roots.

Example fix

// before
modelBuilder.Entity<Session>().ToContainer("state").HasDefaultTimeToLive(300);
modelBuilder.Entity<Token>().ToContainer("state").HasDefaultTimeToLive(900);

// after
const int ContainerTtl = 300;
modelBuilder.Entity<Session>().ToContainer("state").HasDefaultTimeToLive(ContainerTtl);
modelBuilder.Entity<Token>().ToContainer("state").HasDefaultTimeToLive(ContainerTtl);
Defensive patterns

Strategy: validation

Validate before calling

using (var ctx = new MyContext()) { ctx.Model.GetModel(); }
foreach (var grp in ctx.Model.GetEntityTypes()
    .Where(e => e.FindPrimaryKey() != null && e.GetContainer() != null)
    .GroupBy(e => e.GetContainer()))
{
    var ttls = grp.Select(e => e.GetDefaultTimeToLive()).Distinct().ToList();
    Debug.Assert(ttls.Count <= 1, $"Container {grp.Key} has conflicting default TTLs");
}

Try / catch

try { ctx.Model.GetModel(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("default time to live"))
{
    logger.LogError(ex, "Default TTL mismatch in shared Cosmos container");
    throw;
}

Prevention

When it happens

Trigger: Calling HasDefaultTimeToLive(N) with different N on two entity types that resolve to the same container name, surfaced when the model is validated (e.g. first query, EnsureCreated, or dotnet ef database update).

Common situations: One entity configured for time-based document expiry and another in the same container left at a different/no TTL; refactoring an entity into a shared container without copying the TTL setting; environment-specific config where TTL differs by region.

Related errors


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