elsa-workflows/elsa-core · error · InvalidOperationException

Runtime entity definition

Error message

Runtime entity definition '{definition.Name}' declares field '{duplicateField.Key}' more than once.

What it means

Field names within a runtime entity definition must be unique, compared case-insensitively (OrdinalIgnoreCase). Validate groups fields by name and throws when any name appears more than once, since duplicate names make storage and index mapping ambiguous.

Solutions

  1. Remove or rename the duplicated field so names are unique ignoring case.
  2. Deduplicate the field list before validation, e.g. GroupBy(x => x.Name, StringComparer.OrdinalIgnoreCase) and keep one.
  3. If a default field set is merged with user fields, skip defaults whose names already exist.

Example fix

// before
def.Fields.Add(new RuntimeEntityField { Name = "Status" });
def.Fields.Add(new RuntimeEntityField { Name = "status" }); // duplicate ignoring case
// after
def.Fields.Add(new RuntimeEntityField { Name = "Status" });
Defensive patterns

Strategy: validation

Validate before calling

var dupes = definition.Fields.GroupBy(f => f.Name, StringComparer.OrdinalIgnoreCase).Where(g => g.Count() > 1).Select(g => g.Key).ToList();
if (dupes.Count > 0) throw new InvalidOperationException($"Duplicate fields: {string.Join(", ", dupes)}");

Try / catch

try { validator.Validate(definition); }
catch (InvalidOperationException ex) { logger.LogError(ex, "Duplicate fields in {Name}", definition.Name); throw; }

Prevention

When it happens

Trigger: Calling Validate where two or more Fields entries share the same name ignoring case — e.g. 'Status' and 'status'.

Common situations: Merging field lists from multiple configuration sources; appending default fields without checking whether the user already declared them; case differences from copy-paste or JSON config with case-insensitive consumers.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at src/modules/Elsa.Persistence.VNext.Runtime/Services/RuntimeEntityDefinitionValidator.cs:18

using Elsa.Persistence.VNext.Runtime.Models;
using Microsoft.Extensions.Options;

namespace Elsa.Persistence.VNext.Runtime.Services;

public class RuntimeEntityDefinitionValidator(IOptions<RuntimeEntityOptions> options)
{
    public void Validate(RuntimeEntityDefinition definition)
    {
        if (string.IsNullOrWhiteSpace(definition.Name))
            throw new InvalidOperationException("Runtime entity definition name is required.");

        if (definition.Fields.Count == 0)
            throw new InvalidOperationException($"Runtime entity definition '{definition.Name}' must declare at least one field.");

        var duplicateField = definition.Fields.GroupBy(x => x.Name, StringComparer.OrdinalIgnoreCase).FirstOrDefault(x => x.Count() > 1);
        if (duplicateField is not null)
            throw new InvalidOperationException($"Runtime entity definition '{definition.Name}' declares field '{duplicateField.Key}' more than once.");

        var maxIndexedFields = Math.Min(options.Value.MaxIndexedFields, RuntimeEntityPersistenceSchemaProvider.IndexedFieldSlotCount);
        if (definition.Indexes.Count > maxIndexedFields)
            throw new InvalidOperationException($"Runtime entity definition '{definition.Name}' declares {definition.Indexes.Count} indexes, but only {maxIndexedFields} runtime index slots are available.");

        var fields = definition.Fields.Select(x => x.Name).ToHashSet(StringComparer.OrdinalIgnoreCase);
        foreach (var index in definition.Indexes)
        {
            if (!fields.Contains(index.FieldName))
                throw new InvalidOperationException($"Runtime entity definition '{definition.Name}' index '{index.Name}' references unknown field '{index.FieldName}'.");
        }
    }

    public void ValidateInstance(RuntimeEntityDefinition definition, RuntimeEntityInstance instance)
    {
        var dataKeys = instance.Data.Keys.ToHashSet(StringComparer.OrdinalIgnoreCase);
        var missingField = definition.Fields.FirstOrDefault(x => x.IsRequired && !dataKeys.Contains(x.Name));
        if (missingField is not null)

View on GitHub (pinned to fe9217bdfa)