elsa-workflows/elsa-core · error · JsonException

Expected an EndObject token

Error message

Expected an EndObject token

What it means

HttpHeadersConverter.Read builds an HttpHeaders instance from JSON while walking object tokens. When it reaches the end of the JSON buffer without having consumed the expected EndObject token, it throws this JsonException. It means the JSON being deserialized is malformed or truncated - an object was opened but never closed, or a scalar/arrays appeared where object syntax was required.

Solutions

  1. Validate the JSON payload with a parser (e.g. JsonDocument.Parse in a try/catch) before passing it to deserialization to pinpoint the syntax error.
  2. Ensure every object opened in the JSON for HttpHeaders-typed properties is closed with '}'.
  3. Check that the serialized data was not truncated in transit (log/inspect raw payload length and tail).
  4. If using custom serialization pipelines, make sure Utf8JsonReader input spans contain the complete document (utf8 bytes not sliced mid-object).

Example fix

// before
var headers = JsonSerializer.Deserialize<HttpHeaders>(truncatedJson);
// after
JsonDocument doc;
try { doc = JsonDocument.Parse(json); }
catch (JsonException ex) { throw new FormatException($"Malformed JSON payload: {ex.Message}", ex); }
var headers = doc.RootElement.ValueKind == JsonValueKind.Object
    ? JsonSerializer.Deserialize<HttpHeaders>(json)
    : throw new FormatException("Expected a JSON object for HttpHeaders");
Defensive patterns

Strategy: validation

Validate before calling

static bool IsValidHttpHeadersJson(string json)
{
    try
    {
        using var doc = JsonDocument.Parse(json);
        return doc.RootElement.ValueKind == JsonValueKind.Object;
    }
    catch (JsonException) { return false; }
}

Type guard

static bool IsHttpHeadersObject(JsonElement el) => el.ValueKind == JsonValueKind.Object && el.EnumerateObject().All(p => p.Value.ValueKind is JsonValueKind.String or JsonValueKind.Array);

Try / catch

try { var headers = JsonSerializer.Deserialize<HttpHeaders>(json, options); }
catch (JsonException ex) when (ex.Message.Contains("EndObject"))
{
    logger.LogError(ex, "Malformed/truncated JSON for HttpHeaders");
}

Prevention

When it happens

Trigger: Deserializing a payload containing an HttpHeaders-typed property where the JSON object is unterminated (e.g. '{"Authorization": ["Bearer x"]' missing the closing '}'), or where the reader hits end-of-input inside the object, such as during model binding of HTTP header sets in workflows or API endpoints.

Common situations: Truncated request bodies from proxies or clients, hand-crafted JSON in workflow definition files, copy-pasted JSON missing a closing brace, or a converter/serializer mismatch where a non-object value (string/array) is supplied for an HttpHeaders property (that path throws the sibling 'Expected a String or StartArray token' error instead).

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/1ea3fa10fef59dc4. Report an issue: GitHub.

Appendix: source

Thrown at src/modules/Elsa.Http/Serialization/HttpHeadersConverter.cs:51

                case JsonTokenType.StartArray:
                {
                    var values = new List<string>();
                    while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) values.Add(reader.GetString()!);
                    headers.Add(key, values.ToArray());
                    break;
                }
                case JsonTokenType.String:
                {
                    var singleValue = reader.GetString()!;
                    headers.Add(key, new[] { singleValue });
                    break;
                }
                default:
                    throw new JsonException("Expected a String or StartArray token");
            }
        }

        throw new JsonException("Expected an EndObject token");
    }

    /// <inheritdoc />
    public override void Write(Utf8JsonWriter writer, HttpHeaders value, JsonSerializerOptions options)
    {
        writer.WriteStartObject();

        foreach (var header in value)
        {
            writer.WritePropertyName(header.Key);
            writer.WriteStartArray();
            foreach (var headerValue in header.Value) writer.WriteStringValue(headerValue);
            writer.WriteEndArray();
        }

        writer.WriteEndObject();
    }
}

View on GitHub (pinned to fe9217bdfa)