elsa-workflows/elsa-core · error · JsonException
Expected a String or StartArray token
Error message
Expected a String or StartArray token
What it means
After reading a header key, HttpHeadersConverter only accepts either a single String token or a StartArray token (for multiple values). Any other token — number, boolean, nested object, null — triggers JsonException('Expected a String or StartArray token'), because header values must be strings or arrays of strings.
Solutions
- Convert header values to strings (or arrays of strings) in the JSON: {"Content-Length": "123"}
- Fix the producer to serialize header values via ToString before writing JSON
- Validate the payload shape with JsonDocument before calling Deserialize<HttpHeaders>
- Catch JsonException around deserialization and log the offending header key
Example fix
// before
{"Content-Length": 123}
// after
{"Content-Length": "123"} Defensive patterns
Strategy: type-guard
Validate before calling
using var doc = JsonDocument.Parse(json);
foreach (var prop in doc.RootElement.EnumerateObject())
{
var ok = prop.Value.ValueKind == JsonValueKind.String
|| (prop.Value.ValueKind == JsonValueKind.Array && prop.Value.EnumerateArray().All(v => v.ValueKind == JsonValueKind.String));
if (!ok) throw new FormatException($"Header '{prop.Name}' must be a string or array of strings");
} Type guard
bool IsStringOrStringArray(JsonElement v) => v.ValueKind == JsonValueKind.String || (v.ValueKind == JsonValueKind.Array && v.EnumerateArray().All(x => x.ValueKind == JsonValueKind.String));
Try / catch
try { headers = JsonSerializer.Deserialize<HttpHeaders>(json, options); }
catch (JsonException ex)
{
logger.LogWarning(ex, "Header value was not a string or string array");
throw;
} Prevention
- Always convert header values with ToString() before serializing
- Reject numeric/boolean/object header values at your API boundary
- Document that HttpHeaders maps to string | string[] per key in any client SDKs
When it happens
Trigger: Deserializing HttpHeaders where a header value is a non-string/non-array, e.g. {"Content-Length": 123} or {"X-Custom": {"a":1}} — common when JSON was generated by code that didn't stringify values.
Common situations: Client/API payloads built with numeric or boolean header values; serializer output from a different HttpHeaders representation; hand-edited workflow state.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
Related errors
- Expected StartObject token
- Expected a PropertyName token
- The serialization type alias is missing.
- Unsupported console stream value
- The persisted external authentication value could not be…
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/09c26e9d28f8feed.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.Http/Serialization/HttpHeadersConverter.cs:47
// If the next token is not a StartArray token, then we expect a String token.
switch (reader.TokenType)
{
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();
}View on GitHub (pinned to fe9217bdfa)