elsa-workflows/elsa-core · error · InvalidOperationException

AI conversation entity metadata was not found.

Error message

AI conversation entity metadata was not found.

What it means

EFCoreAIConversationCleanup resolves the mapped table name for AIConversationRecord from the EF Core model. If FindEntityType returns null, the AIConversationRecord entity was never added to AIDbContext's model, so cleanup throws InvalidOperationException. This usually indicates a mismatched or misconfigured model.

Solutions

  1. Register AIConversationRecord in the AIDbContext model (DbSet and/or OnModelCreating mapping).
  2. Ensure cleanup runs against the real AIDbContext, not a substitute context.
  3. Regenerate/rebuild the EF model after package upgrades.
  4. Verify no custom IModelConfiguration removes the entity mapping.

Example fix

// before
class MyAIDbContext : DbContext { }
// after
class MyAIDbContext : DbContext {
    public DbSet<AIConversationRecord> Conversations => Set<AIConversationRecord>();
}
Defensive patterns

Strategy: validation

Validate before calling

var entityType = dbContext.Model.FindEntityType(typeof(AIConversationRecord));
if (entityType is null) throw new InvalidOperationException("AIConversationRecord not mapped in AIDbContext model");

Type guard

bool IsConversationMapped(AIDbContext db) => db.Model.FindEntityType(typeof(AIConversationRecord)) is not null;

Try / catch

try { await cleanup.RunAsync(ct); } catch (InvalidOperationException ex) when (ex.Message.Contains("entity metadata")) { logger.LogError(ex, "AIDbContext model missing AIConversationRecord"); }

Prevention

When it happens

Trigger: Running cleanup against an AIDbContext whose model does not include AIConversationRecord (e.g. wrong DbContext instance, model built without the entity, or an outdated/overridden OnModelCreating).

Common situations: Using a hand-built ModelBuilder in tests that omits the entity; replacing AIDbContext with a custom context that forgets the DbSet/mapping; stale model caches after upgrading the module.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13). Data as JSON: /api/errors/48ba9093cc30580a. Report an issue: GitHub.

Appendix: source

Thrown at src/modules/Elsa.AI.Persistence.EFCore/Services/EFCoreAIConversationCleanup.cs:48

            var tableName = ResolveConversationTableName(dbContext);
            var sql = $@"DELETE FROM {QuoteSqliteIdentifier(tableName)}
WHERE ""RetentionMode"" = {{0}}
  AND ""RetentionExpiresAt"" IS NOT NULL
  AND ""RetentionExpiresAt"" <= {{1}}";
            return await dbContext.Database.ExecuteSqlRawAsync(
                sql,
                [configuredRetentionMode, now],
                cancellationToken);
        }

        return await dbContext.Conversations
            .Where(x => x.RetentionMode == configuredRetentionMode && x.RetentionExpiresAt != null && x.RetentionExpiresAt <= now)
            .ExecuteDeleteAsync(cancellationToken);
    }

    private static string ResolveConversationTableName(AIDbContext dbContext)
    {
        var entityType = dbContext.Model.FindEntityType(typeof(AIConversationRecord)) ?? throw new InvalidOperationException("AI conversation entity metadata was not found.");
        return entityType.GetTableName() ?? throw new InvalidOperationException("AI conversation table metadata was not found.");
    }

    private static bool IsSqliteProvider(string? providerName) =>
        providerName?.Contains("Sqlite", StringComparison.OrdinalIgnoreCase) == true;

    private static string QuoteSqliteIdentifier(string identifier) => $"\"{identifier.Replace("\"", "\"\"", StringComparison.Ordinal)}\"";
}

View on GitHub (pinned to fe9217bdfa)