elsa-workflows/elsa-core · error · JsonException

Failed to parse JsonDocument

Error message

Failed to parse JsonDocument

What it means

ActivityJsonConverter.Read converts incoming JSON into an IActivity. It first parses the JSON token into a JsonDocument; if TryParseValue fails (malformed or unexpected JSON), it throws this JsonException. It is a low-level guard ensuring only valid JSON objects are processed for activity deserialization.

Solutions

  1. Validate/pretty-check the JSON source with a parser before handing it to Elsa's serializer.
  2. Fix malformed JSON (missing braces/commas, truncation) at the source of the string.
  3. Ensure you deserialize with System.Text.Json options compatible with the payload (e.g. no embedded comments unless configured).
  4. If data comes from storage, re-export the workflow definition to regenerate clean JSON.

Example fix

// before
var activity = JsonSerializer.Deserialize<IActivity>(truncatedJson);

// after
using var doc = JsonDocument.Parse(json); // throws a precise line/position error first
var activity = JsonSerializer.Deserialize<IActivity>(json);
Defensive patterns

Strategy: validation

Validate before calling

try { using var _ = JsonDocument.Parse(json); } catch (JsonException ex) { throw new InvalidOperationException("Workflow JSON is malformed", ex); }
var activity = JsonSerializer.Deserialize<IActivity>(json, options);

Type guard

static bool IsValidJson(string s) { try { using var d = JsonDocument.Parse(s); return true; } catch (JsonException) { return false; } }

Try / catch

try { var activity = JsonSerializer.Deserialize<IActivity>(json); } catch (JsonException ex) when (ex.Message.Contains("Failed to parse JsonDocument")) { log.LogError(ex, "Malformed activity JSON"); /* repair or re-export payload */ }

Prevention

When it happens

Trigger: Deserializing workflow/activity JSON where the Utf8JsonReader is not positioned on a valid JSON value (truncated JSON, invalid syntax, or reading a primitive where a document is required) at ActivityJsonConverter.Read (src/modules/Elsa.Workflows.Core/Serialization/Converters/ActivityJsonConverter.cs:28).

Common situations: Passing hand-edited workflow JSON from files or database blobs that got corrupted; copying JSON with trailing commas or comments; deserializing a partial HTTP response body.

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


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

Appendix: source

Thrown at src/modules/Elsa.Workflows.Core/Serialization/Converters/ActivityJsonConverter.cs:28

using Microsoft.Extensions.Logging;

namespace Elsa.Workflows.Serialization.Converters;

/// <summary>
/// (De)serializes objects of type <see cref="IActivity"/>.
/// </summary>
public class ActivityJsonConverter(
    IActivityRegistry activityRegistry,
    IExpressionDescriptorRegistry expressionDescriptorRegistry,
    ActivityWriter activityWriter,
    IServiceProvider serviceProvider)
    : JsonConverter<IActivity>
{
    /// <inheritdoc />
    public override IActivity Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (!JsonDocument.TryParseValue(ref reader, out var doc))
            throw new JsonException("Failed to parse JsonDocument");

        var activityRoot = doc.RootElement;
        var activityTypeName = GetActivityDetails(activityRoot, out var activityTypeVersion, out var activityDescriptor);
        var notFoundActivityTypeName = ActivityTypeNameHelper.GenerateTypeName<NotFoundActivity>();

        // If the activity type is a NotFoundActivity, try to extract the original activity type name and version.
        if (activityTypeName.Equals(notFoundActivityTypeName) && activityRoot.TryGetProperty("originalActivityJson", out var originalActivityJson))
        {
            activityRoot = JsonDocument.Parse(originalActivityJson.GetString()!).RootElement;
            activityTypeName = GetActivityDetails(activityRoot, out activityTypeVersion, out activityDescriptor);
        }

        var clonedOptions = GetClonedOptions(options);
        // If the activity type is not found, create a NotFoundActivity instead.
        if (activityDescriptor == null)
        {
            var notFoundActivityDescriptor = activityRegistry.Find<NotFoundActivity>()!;
            var notFoundActivityResult = JsonActivityConstructorContextHelper.CreateActivity<NotFoundActivity>(notFoundActivityDescriptor, activityRoot, clonedOptions);

View on GitHub (pinned to fe9217bdfa)