elsa-workflows/elsa-core · error · InvalidOperationException
Output converter ' ' cannot use open-generic source or…
Error message
Output converter '{descriptor.Id}' cannot use open-generic source or result types. What it means
Thrown by OutputConverterRegistry.ValidateDescriptor when a converter's SourceType or ResultType contains open generic parameters (e.g., typeof(IEnumerable<>)). The runtime must match concrete runtime value types against the descriptor, so open generics cannot be resolved or dispatched.
Solutions
- Close the generic types: use typeof(IEnumerable<int>) or the concrete closed type instead of typeof(IEnumerable<>).
- Call MakeGenericType(concreteArgs) on the open type when constructing the descriptor at registration time.
- Register one descriptor per concrete type combination you need to support.
Example fix
// before descriptor.SourceType = typeof(IEnumerable<>); // after descriptor.SourceType = typeof(IEnumerable<int>); // or, dynamically: descriptor.SourceType = typeof(IEnumerable<>).MakeGenericType(elementType);
Defensive patterns
Strategy: validation
Validate before calling
if (descriptor.SourceType.ContainsGenericParameters || descriptor.ResultType.ContainsGenericParameters)
throw new InvalidOperationException("Converter types must be closed (call MakeGenericType before registering)."); Type guard
bool UsesClosedTypes(OutputConverterDescriptor d) => !d.SourceType.ContainsGenericParameters && !d.ResultType.ContainsGenericParameters;
Try / catch
try
{
services.AddOutputConverter(descriptor);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("open-generic"))
{
logger.LogCritical(ex, "Converter {Id} uses open generics; register closed types instead", descriptor.Id);
throw;
} Prevention
- Remember descriptors must describe concrete runtime types; generics only exist at compile time.
- Use MakeGenericType in a registration loop over the concrete types you support.
- Avoid generic converter classes unless you also generate closed descriptors for them.
When it happens
Trigger: Registering a converter with SourceType = typeof(T) / typeof(List<>) or a ResultType that is a generic type definition, typically when writing a generic converter class and copying its generic type parameters into the descriptor.
Common situations: Authors wanting 'convert anything to string' converters using generic parameters, converters ported from open-generic classes, and reflection-built descriptors where MakeGenericType was never called.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Output converter IDs cannot be empty.
- A configuration key is required.
- A conversation ID is required. (Parameter 'conversation')
- A conversation ID is required. (Parameter 'conversation')
- A conversation user ID is required. (Parameter…
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/ab72d378f109996f.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.Workflows.Core/Services/OutputConverterRegistry.cs:70
if (!string.Equals(id, registration.ServiceKey, StringComparison.Ordinal))
throw new InvalidOperationException($"Output converter registration '{id}' must use the Converter ID as its service key.");
}
var duplicate = registrations
.GroupBy(x => x.Descriptor.Id, StringComparer.OrdinalIgnoreCase)
.FirstOrDefault(x => x.Count() > 1);
if (duplicate != null)
throw new InvalidOperationException($"Output converter ID '{duplicate.Key}' is registered more than once or differs from another registration only by case.");
}
internal static void ValidateDescriptor(OutputConverterDescriptor descriptor)
{
if (string.IsNullOrWhiteSpace(descriptor.Id))
throw new InvalidOperationException("Output converter IDs cannot be empty.");
if (descriptor.SourceType.ContainsGenericParameters || descriptor.ResultType.ContainsGenericParameters)
throw new InvalidOperationException($"Output converter '{descriptor.Id}' cannot use open-generic source or result types.");
}
}
View on GitHub (pinned to fe9217bdfa)