elsa-workflows/elsa-core · error · JsonException
Workflow JSON type alias resolved to non-instantiable type
Error message
Workflow JSON type alias resolved to non-instantiable type '{targetType}'. What it means
Thrown by PolymorphicObjectConverter.GetInstantiableTargetType when a JSON type alias resolves to a target type that is neither concrete nor has a known instantiable collection substitute. Interfaces and abstract classes cannot be instantiated with 'new', so the serializer refuses instead of producing an invalid object. It indicates the type alias or $type in the workflow JSON points at a type the resolver cannot materialize.
Solutions
- Register a concrete type for the alias/interface via the type resolver or type-alias registration so GetInstantiableTargetType returns an instantiable type
- Change the workflow/activity property to a concrete, instantiable type instead of an interface or abstract class
- If the target is a collection interface, ensure TryGetInstantiableCollectionType can map it (e.g. IEnumerable<T> -> List<T>) or add a custom mapping
- Update stale $type values in the stored workflow JSON after type renames
Example fix
// before
public IMyHandler Handler { get; set; }
// after
services.AddTypeAlias<IMyHandler, MyHandler>(); // register concrete instantiable type
// or
public MyHandler Handler { get; set; } Defensive patterns
Strategy: validation
Validate before calling
var targetType = ResolveAlias(alias); // your alias resolution
if (targetType.IsInterface || targetType.IsAbstract)
throw new InvalidOperationException($"Alias '{alias}' must map to a concrete type; register one in the type resolver."); Type guard
static bool IsInstantiable(Type t) => !t.IsInterface && !t.IsAbstract;
Prevention
- Register concrete implementations for every interface/abstract type used in activity properties
- Keep $type aliases in workflow JSON in sync after refactors; validate stored definitions after renames
- Add a startup smoke test that deserializes representative workflow definitions
When it happens
Trigger: Deserializing workflow JSON whose $type/type alias resolves to an interface (e.g. an interface-typed property without a concrete alias mapping) or an abstract class, and the type is not registered in the type resolver nor mapped by SerializationTypeResolver.TryGetInstantiableCollectionType (e.g. IEnumerable<T>).
Common situations: Custom activities with properties typed as interfaces or abstract classes where the concrete implementation was never registered via ITypeAliasRegistry or AddActivationConstructorBody; renamed/refactored types so the alias now maps to an abstract base; custom SerializationTypeResolver missing a collection instantiation mapping.
Related errors
- Unknown serialization type alias
- Cannot deserialize to .
- Failed to deserialize
- The binding to activity type
- Expected number or string.
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/d1bdfd9653356fb4.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.Workflows.Core/Serialization/Converters/PolymorphicObjectConverter.cs:406
"Value of type {PayloadType} has no registered serialization alias, so it is stored without a type discriminator and will be read back as a property bag with camel-cased keys instead of as {PayloadType}. " +
"Register it during startup with AddTypeAlias<{PayloadTypeName}>(), or use a Dictionary<string, object> if a property bag is what you intend.",
type,
type,
type.Name);
}
private static Type GetInstantiableTargetType(Type targetType)
{
if (targetType.ContainsGenericParameters)
throw new JsonException($"Workflow JSON type alias resolved to open generic type '{targetType}'.");
if (!targetType.IsInterface && !targetType.IsAbstract)
return targetType;
if (SerializationTypeResolver.TryGetInstantiableCollectionType(targetType, out var instantiableCollectionType))
return instantiableCollectionType;
throw new JsonException($"Workflow JSON type alias resolved to non-instantiable type '{targetType}'.");
}
private static object ReadPrimitive(ref Utf8JsonReader reader, JsonSerializerOptions options)
{
return (reader.TokenType switch
{
JsonTokenType.True => true,
JsonTokenType.False => false,
JsonTokenType.Number when reader.TryGetInt64(out var l) => l,
JsonTokenType.Number => reader.GetDouble(),
JsonTokenType.String => reader.GetString(),
JsonTokenType.Null => null,
_ => throw new JsonException("Not a primitive type.")
})!;
}
private object ReadObject(ref Utf8JsonReader reader, JsonSerializerOptions options)
{View on GitHub (pinned to fe9217bdfa)