elsa-workflows/elsa-core · error · JsonException

Missing property ' ' or

Error message

Missing property '{name}' or '{alt}'

What it means

Thrown by ConnectionJsonConverter.Read when the connection JSON object contains neither the lowercase nor PascalCase form of a required property ('source'/'Source', 'target'/'Target'). The converter's Get helper tries both spellings before failing. It means the connection payload is structurally incomplete.

Solutions

  1. Ensure each connection object has both 'source' and 'target' properties.
  2. If your producer uses different key names, rename them to source/target before deserialization or write a custom converter.
  3. Inspect the offending JSON object to confirm which property is missing.
  4. Validate workflow definitions against the expected connection schema before ingestion.

Example fix

// before
{"connections": [{"source": {"activity":"a"}}]} // missing target
// after
{"connections": [{"source": {"activity":"a"}, "target": {"activity":"b"}}]}
Defensive patterns

Strategy: validation

Validate before calling

using var doc = JsonDocument.Parse(json);
foreach (var conn in doc.RootElement.GetProperty("connections").EnumerateArray())
{
    if (!conn.TryGetProperty("source", out _) || !conn.TryGetProperty("target", out _))
        throw new ArgumentException("Each connection must contain 'source' and 'target'.");
}

Type guard

static bool HasConnectionEndpoints(JsonElement conn) =>
    conn.ValueKind == JsonValueKind.Object && conn.TryGetProperty("source", out _) && conn.TryGetProperty("target", out _);

Try / catch

try { var flowchart = JsonSerializer.Deserialize<Activities.Flowchart>(json); }
catch (JsonException ex) when (ex.Message.StartsWith("Missing property"))
{
    logger.LogError(ex, "Connection JSON is missing source/target: {Message}", ex.Message);
}

Prevention

When it happens

Trigger: Deserializing a Connection whose JSON object lacks 'source' or 'target' keys (or their capitalized variants), e.g. an object with only a source or with entirely different key names.

Common situations: Hand-authored workflow JSON missing one endpoint; external tools exporting connections with different property names (e.g. 'from'/'to'); API clients posting partial connection objects.

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/47a81039699946f3. Report an issue: GitHub.

Appendix: source

Thrown at src/modules/Elsa.Workflows.Core/Activities/Flowchart/Serialization/ConnectionJsonConverter.cs:34

    public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Connection);

    /// <inheritdoc />
    public override Connection? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (!JsonDocument.TryParseValue(ref reader, out var doc))
            throw new JsonException("Failed to parse JsonDocument");

        var root = doc.RootElement;

        // case‐insensitive get
        JsonElement Get(string name)
        {
            if (root.TryGetProperty(name, out var e))
                return e;
            var alt = char.ToUpperInvariant(name[0]) + name.Substring(1);
            if (root.TryGetProperty(alt, out e))
                return e;
            throw new JsonException($"Missing property '{name}' or '{alt}'");
        }

        var sourceElement = Get("source");
        var targetElement = Get("target");

        // now inside sourceElement and targetElement, their children
        // are again PascalCased (“Activity”, “Port”), so do the same thing:

        string GetId(JsonElement container, string propName)
        {
            if (container.TryGetProperty(propName, out var p))
                return p.GetString()!;
            var alt = char.ToUpperInvariant(propName[0]) + propName.Substring(1);
            return container.GetProperty(alt).GetString()!;
        }

        string? GetPort(JsonElement container, string propName)
        {

View on GitHub (pinned to fe9217bdfa)