elsa-workflows/elsa-core · error · JsonException
Failed to parse JsonDocument
Error message
Failed to parse JsonDocument
What it means
Thrown in FlowchartJsonConverter.Read when the JSON token for a Flowchart activity cannot be parsed into a JsonDocument. This guards against malformed JSON before reading id, nodeId, name, type, version, and connections properties during workflow definition deserialization.
Solutions
- Parse and validate the definition JSON independently (JsonDocument.Parse) to pinpoint the malformed fragment.
- Check storage/transport layers for truncation or encoding issues (column length limits, charset mismatches).
- Ensure the flowchart is serialized as a JSON object, not wrapped in a string.
- Catch JsonException around deserialization and include the failing JSON snippet in diagnostics.
Example fix
// before var flowchart = JsonSerializer.Deserialize<Activities.Flowchart>(json); // throws on malformed json // after using var doc = JsonDocument.Parse(json); // validate first var flowchart = json.Deserialize<Activities.Flowchart>();
Defensive patterns
Strategy: try-catch
Validate before calling
using var doc = JsonDocument.Parse(definitionJson); // pre-validate whole definition
Type guard
static bool IsJsonObjectToken(string json) { try { using var d = JsonDocument.Parse(json); return d.RootElement.ValueKind == JsonValueKind.Object; } catch (JsonException) { return false; } } Try / catch
try { var flowchart = JsonSerializer.Deserialize<Activities.Flowchart>(json); }
catch (JsonException ex)
{
logger.LogError(ex, "Malformed flowchart JSON: {Message}", ex.Message);
throw new WorkflowDefinitionFormatException("Definition contains invalid flowchart JSON", ex);
} Prevention
- Round-trip test definition serialization/deserialization in CI.
- Ensure transports do not truncate payloads (check body size limits, column sizes).
- Never wrap activity JSON in an extra string layer.
When it happens
Trigger: Deserializing a workflow definition where the flowchart node's JSON is syntactically invalid — truncated JSON, wrong token type (string/number instead of object), or encoding corruption.
Common situations: Corrupted stored workflow definitions; truncated HTTP request bodies; hand-edited JSON with syntax errors; systems that re-encode the payload incorrectly during round-trips.
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Failed to parse JsonDocument
- Missing property ' ' or
- Failed to parse JsonDocument
- Cannot deserialize to .
- Failed to deserialize
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/665e4b9092c13106.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.Workflows.Core/Activities/Flowchart/Serialization/FlowchartJsonConverter.cs:27
using Elsa.Common.Serialization;
namespace Elsa.Workflows.Activities.Flowchart.Serialization;
/// <summary>
/// A JSON converter for <see cref="Activities.Flowchart"/>.
/// </summary>
[UsedImplicitly]
public class FlowchartJsonConverter(IIdentityGenerator identityGenerator, ISerializationTypeRegistry workflowJsonTypeRegistry, ILoggerFactory loggerFactory) : JsonConverter<Activities.Flowchart>
{
private const string AllActivitiesKey = "allActivities";
private const string AllConnectionsKey = "allConnections";
private const string NotFoundConnectionsKey = "notFoundConnections";
/// <inheritdoc />
public override Activities.Flowchart Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (!JsonDocument.TryParseValue(ref reader, out var doc))
throw new JsonException("Failed to parse JsonDocument");
var id = doc.RootElement.TryGetProperty("id", out var idAttribute) ? idAttribute.GetString()! : identityGenerator.GenerateId();
var nodeId = doc.RootElement.TryGetProperty("nodeId", out var nodeIdAttribute) ? nodeIdAttribute.GetString() : null;
var name = doc.RootElement.TryGetProperty("name", out var nameElement) ? nameElement.GetString() : null;
var type = doc.RootElement.TryGetProperty("type", out var typeElement) ? typeElement.GetString() : null;
var version = doc.RootElement.TryGetProperty("version", out var versionElement) ? versionElement.GetInt32() : 1;
var runAsynchronously = doc.RootElement.TryGetProperty("runAsynchronously", out var runAsyncElement) && runAsyncElement.GetBoolean();
var connectionsElement = doc.RootElement.TryGetProperty("connections", out var connectionsEl) ? connectionsEl : default;
var activitiesElement = doc.RootElement.TryGetProperty("activities", out var activitiesEl) ? activitiesEl : default;
var activities = activitiesElement.ValueKind != JsonValueKind.Undefined ? activitiesElement.Deserialize<ICollection<IActivity>>(options) ?? new List<IActivity>() : new List<IActivity>();
var activityDictionary = activities.ToDictionary(x => x.Id);
var connections = DeserializeConnections(connectionsElement, activityDictionary, options);
var notFoundConnections = GetNotFoundConnections(doc.RootElement, activityDictionary, connections, options);
var connectionsToRestore = FindConnectionsThatCanBeRestored(notFoundConnections, activities);
var connectionComparer = new ConnectionComparer();
var connectionsWithRestoredOnes = connections.Except(notFoundConnections, connectionComparer).Union(connectionsToRestore, connectionComparer).ToList();
View on GitHub (pinned to fe9217bdfa)