elsa-workflows/elsa-core · error · TypeConversionException

Failed to deserialize

Error message

Failed to deserialize {stringValue} to {underlyingTargetType}

What it means

ObjectConverter.ConvertTo attempts to JsonSerializer.Deserialize a string that starts with '{' or '[' into the target type. If deserialization fails for any reason it wraps the exception in a TypeConversionException reporting that it failed to deserialize the string value to the underlying target type.

Solutions

  1. Validate that the string is well-formed JSON and its shape matches the target type before conversion
  2. Use System.Text.Json to test-parse in a diagnostic step: JsonSerializer.Deserialize<T>(json) and inspect the JsonException for the offending path
  3. Enable strict mode off (default) to get a default value instead of an exception, or fix the payload in strict mode
  4. Check serializerOptions (naming policy, converters) match the payload conventions

Example fix

// before
var item = ObjectConverter.ConvertTo<TaskList>(rawJson, typeof(TaskList));
// after
if (typeof(TaskList).IsAssignableFrom(typeof(TaskList)) && !IsValidJson<TaskList>(rawJson))
    throw new FormatException("Payload shape does not match TaskList");
var item = ObjectConverter.ConvertTo<TaskList>(rawJson, typeof(TaskList));
Defensive patterns

Strategy: validation

Validate before calling

bool isValidJson<T>(string s) { try { JsonSerializer.Deserialize<T>(s); return true; } catch (JsonException) { return false; } }

Type guard

bool looksLikeJson(string s) => !string.IsNullOrWhiteSpace(s) && (s.TrimStart().StartsWith('{') || s.TrimStart().StartsWith('['));

Try / catch

try { value = ObjectConverter.ConvertTo(targetType, json); }
catch (TypeConversionException ex) { logger.LogError(ex, "Deserialization failed for {Target}", targetType); value = null; }

Prevention

When it happens

Trigger: Converting a string starting with '{' or '[' (via ConvertTo/ObjectConverter) into a target type whose JSON does not match — e.g. passing a JSON array into a target expecting an object, malformed JSON, or a property name/type mismatch under the configured serializer options.

Common situations: Workflow inputs supplied as JSON text into object-typed variables, malformed JSON from clipboard/designer, or changing case-sensitivity/serializer options so previously matching payloads no longer deserialize.

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/2d7e58ef02377272. Report an issue: GitHub.

Appendix: source

Thrown at src/modules/Elsa.Expressions/Helpers/ObjectConverter.cs:170

        {
            var stringValue = (string)value;

            if (underlyingTargetType == typeof(byte[]))
            {
                // Byte arrays are serialized to base64, so in this case, we convert the string back to the requested target type of byte[].
                return Convert.FromBase64String(stringValue);
            }

            try
            {
                var firstChar = stringValue.TrimStart().FirstOrDefault();

                if (firstChar is '{' or '[')
                    return JsonSerializer.Deserialize(stringValue, underlyingTargetType, serializerOptions);
            }
            catch (Exception e)
            {
                throw new TypeConversionException($"Failed to deserialize {stringValue} to {underlyingTargetType}", value, underlyingTargetType, e);
            }
        }

        if (targetType == typeof(object))
            return value;

        if (underlyingTargetType.IsInstanceOfType(value))
            return value;

        if (underlyingSourceType == underlyingTargetType)
            return value;

        if (IsDateType(underlyingSourceType) && IsDateType(underlyingTargetType))
            return ConvertAnyDateType(value, underlyingTargetType);

        var internalSerializerOptions = InternalSerializerOptions;

        if (typeof(IDictionary<string, object>).IsAssignableFrom(underlyingSourceType) && (underlyingTargetType.IsClass || underlyingTargetType.IsInterface))

View on GitHub (pinned to fe9217bdfa)