elsa-workflows/elsa-core · error · JsonException

Expected start of object.

Error message

Expected start of object.

What it means

PolymorphicDictionaryConverter.Read deserializes JSON objects into IDictionary<string,object> with polymorphic values. It requires the current token to be JsonTokenType.StartObject; otherwise it throws JsonException("Expected start of object."). This guards the contract that dictionary targets must map to JSON objects.

Solutions

  1. Fix the JSON payload so the dictionary field is a JSON object (key/value pairs).
  2. Change the property type to match the actual payload shape if arrays are legitimate.
  3. Validate the payload shape before deserialization.
  4. Migrate legacy stored state data to the object shape expected by the current model.

Example fix

// before
var d = JsonSerializer.Deserialize<IDictionary<string, object>>("[1,2,3]");

// after
var d = JsonSerializer.Deserialize<IDictionary<string, object>>("{\"a\":1}");
Defensive patterns

Strategy: validation

Validate before calling

using var doc = JsonDocument.Parse(json);
if (doc.RootElement.ValueKind != JsonValueKind.Object)
    throw new InvalidOperationException("Expected a JSON object for dictionary deserialization");

Type guard

static bool IsJsonObjectString(string s) { try { using var d = JsonDocument.Parse(s); return d.RootElement.ValueKind == JsonValueKind.Object; } catch { return false; } }

Try / catch

try { var d = JsonSerializer.Deserialize<IDictionary<string, object>>(json); } catch (JsonException ex) when (ex.Message == "Expected start of object.") { log.LogError(ex, "Dictionary payload is not a JSON object"); }

Prevention

When it happens

Trigger: Deserializing a field declared as IDictionary<string,object> (or a dictionary-like property) whose JSON value is an array, string, number, or null instead of an object (src/modules/Elsa.Workflows.Core/Serialization/Converters/PolymorphicDictionaryConverter.cs:25).

Common situations: Workflow state where a dictionary property was previously stored as a JSON array and the model changed; hand-written JSON for variables/settings using wrong shape; API clients posting arrays where objects are expected.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

Thrown at src/modules/Elsa.Workflows.Core/Serialization/Converters/PolymorphicDictionaryConverter.cs:25

/// <summary>
/// A converter that can convert a dictionary of string keys and object values while preserving the object type.
/// </summary>
public class PolymorphicDictionaryConverter : JsonConverter<IDictionary<string, object>>
{
    private readonly JsonConverter<object> _objectConverter;

    /// <inheritdoc />
    public PolymorphicDictionaryConverter(JsonSerializerOptions options, ISerializationTypeRegistry workflowJsonTypeRegistry)
    {
        var factory = (JsonConverterFactory)(options.Converters.FirstOrDefault(x => x is PolymorphicObjectConverterFactory) ?? new PolymorphicObjectConverterFactory(workflowJsonTypeRegistry));
        _objectConverter = (JsonConverter<object>)factory.CreateConverter(typeof(object), options)!;
    }

    /// <inheritdoc />
    public override IDictionary<string, object> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType != JsonTokenType.StartObject)
            throw new JsonException("Expected start of object.");

        var dictionary = new Dictionary<string, object>();

        while (reader.Read())
        {
            if (reader.TokenType == JsonTokenType.EndObject)
                return dictionary;

            var key = reader.GetString()!;
            reader.Read();
            var value = _objectConverter.Read(ref reader, typeof(object), options)!;
            dictionary.Add(key, value);
        }

        throw new JsonException("Expected end of object.");
    }

    /// <inheritdoc />

View on GitHub (pinned to fe9217bdfa)