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

  1. Register a concrete type for the alias/interface via the type resolver or type-alias registration so GetInstantiableTargetType returns an instantiable type
  2. Change the workflow/activity property to a concrete, instantiable type instead of an interface or abstract class
  3. If the target is a collection interface, ensure TryGetInstantiableCollectionType can map it (e.g. IEnumerable<T> -> List<T>) or add a custom mapping
  4. 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

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


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)