elsa-workflows/elsa-core · error · BpmnBindingException

The binding to activity type

Error message

The binding to activity type '{activityType}' could not be deserialized: {exception.Message}

What it means

The binding's assembled input JSON could not be deserialized into the target Elsa activity. Elsa's activity serializer threw a JsonException or NotSupportedException, and the binder rethrows it as a BpmnBindingException naming the activity type.

Solutions

  1. Read the inner message (appended to this error) to find which JSON member failed, then correct that input's JSON value in the binding to match the activity property's type.
  2. Check the activity type's declared input properties and ensure each JSON value matches (objects for complex types, proper enum strings, etc.).
  3. If a module upgrade changed a property's type, update the binding inputs to the new shape.

Example fix

// before
<elsa:input name="Recipients">"a@b.c"</elsa:input>  // activity expects an array
// after
<elsa:input name="Recipients">["a@b.c"]</elsa:input>
Defensive patterns

Strategy: validation

Validate before calling

foreach (var raw in inputElements)
{
    var text = raw.Value?.Trim();
    if (!string.IsNullOrEmpty(text))
        JsonNode.Parse(text); // throws early with position details
}

Type guard

static bool IsValidJson(string? s)
{
    if (string.IsNullOrWhiteSpace(s)) return true;
    try { JsonNode.Parse(s); return true; } catch (JsonException) { return false; }
}

Try / catch

try { await importer.ImportAsync(bpmn); }
catch (BpmnBindingException ex) when (ex.Message.Contains("could not be deserialized"))
{
    logger.LogError(ex, "Binding JSON does not match the activity type shape.");
}

Prevention

When it happens

Trigger: BpmnActivityBindingFormat.Read calls activitySerializer.Deserialize(activityJson) and the serializer throws JsonException or NotSupportedException (e.g. a JSON value whose shape doesn't match the activity property type, or an unsupported type).

Common situations: A binding input value has the right JSON syntax but the wrong shape (string where object expected, number where enum string expected); input JSON is valid XML text but not the shape the activity expects; a module version changed the property type.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at src/modules/Elsa.Bpmn.Interchange/Binding/BpmnActivityBindingFormat.cs:224

            var name = AttributeOf(input, InputNameAttributeName)
                       ?? throw new BpmnBindingException($"An <{NamespacePrefix}:{InputElementName}> element of the '{activityType}' binding declares no '{InputNameAttributeName}'.");

            if (!seenInputNameSet.Add(name))
                throw new BpmnBindingException($"The '{activityType}' binding declares the input '{name}' more than once. Each <{NamespacePrefix}:{InputElementName}> must name a distinct input.");

            seenInputNames.Add(name);
            activityJson[name] = Parse(input.Value, name, activityType);
        }

        IActivity activity;

        try
        {
            activity = activitySerializer.Deserialize(activityJson.ToJsonString());
        }
        catch (Exception exception) when (exception is JsonException or NotSupportedException)
        {
            throw new BpmnBindingException($"The binding to activity type '{activityType}' could not be deserialized: {exception.Message}");
        }

        // Elsa's activity serializer answers an unregistered type with a NotFoundActivity rather than throwing, and
        // that placeholder only fails once it executes — by which time the workflow has already started and the
        // process is mid-flight. Refusing at bind time turns "this .bpmn needs a module you have not installed" into a
        // sentence naming the type, at the point where someone can still do something about it.
        if (activity is NotFoundActivity)
            throw new BpmnBindingException($"The binding names activity type '{activityType}', which is not registered in this application. Install or enable the module providing it before importing this document.");

        // Elsa's own JSON deserialization ignores a member the target type does not declare, so a mistyped or
        // stale input name would otherwise import silently as an activity missing that configuration, with no
        // diagnostic anywhere. IActivityDescriber.GetInputProperties is the same enumeration Write reads from and
        // ActivityDescriptor.Inputs is built from, so a name is accepted here exactly when Write could have produced
        // it.
        if (seenInputNames.Count > 0)
        {
            var declaredInputNames = activityDescriber.GetInputProperties(activity.GetType())
                .Select(property => JsonNamingPolicy.CamelCase.ConvertName(property.Name))

View on GitHub (pinned to fe9217bdfa)