JamesNK/Newtonsoft.Json · error · JsonSerializationException

Converter cannot read JSON with the specified existing value

Error message

Converter cannot read JSON with the specified existing value. {0} is required.

What it means

Thrown by the sealed override JsonConverter<T>.ReadJson when the existingValue argument is non-null but not assignable to the converter's generic type T. The base sealed method validates the existing value before forwarding it as a typed T? to the user ReadJson overload, so a mismatched existing value cannot cause an invalid cast inside the typed method. It is a JsonSerializationException naming the required type T.

Source

Thrown at Src/Newtonsoft.Json/JsonConverter.cs:121

        /// <param name="writer">The <see cref="JsonWriter"/> to write to.</param>
        /// <param name="value">The value.</param>
        /// <param name="serializer">The calling serializer.</param>
        public abstract void WriteJson(JsonWriter writer, T? value, JsonSerializer serializer);

        /// <summary>
        /// Reads the JSON representation of the object.
        /// </summary>
        /// <param name="reader">The <see cref="JsonReader"/> to read from.</param>
        /// <param name="objectType">Type of the object.</param>
        /// <param name="existingValue">The existing value of object being read.</param>
        /// <param name="serializer">The calling serializer.</param>
        /// <returns>The object value.</returns>
        public sealed override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
        {
            bool existingIsNull = existingValue == null;
            if (!(existingIsNull || existingValue is T))
            {
                throw new JsonSerializationException("Converter cannot read JSON with the specified existing value. {0} is required.".FormatWith(CultureInfo.InvariantCulture, typeof(T)));
            }
            return ReadJson(reader, objectType, existingIsNull ? default : (T?)existingValue, !existingIsNull, serializer);
        }

        /// <summary>
        /// Reads the JSON representation of the object.
        /// </summary>
        /// <param name="reader">The <see cref="JsonReader"/> to read from.</param>
        /// <param name="objectType">Type of the object.</param>
        /// <param name="existingValue">The existing value of object being read. If there is no existing value then <c>null</c> will be used.</param>
        /// <param name="hasExistingValue">The existing value has a value.</param>
        /// <param name="serializer">The calling serializer.</param>
        /// <returns>The object value.</returns>
        public abstract T? ReadJson(JsonReader reader, Type objectType, T? existingValue, bool hasExistingValue, JsonSerializer serializer);

        /// <summary>
        /// Determines whether this instance can convert the specified object type.
        /// </summary>

View on GitHub (pinned to 4f73e74372)

Solutions

  1. Restrict CanConvert to exactly T so the converter is not chosen for mismatched existing-value types.
  2. Pass null/default existingValue when populating through a converter, or ensure the object being populated is of type T.
  3. If you need broader handling, subclass JsonConverter (non-generic) and implement ReadJson with manual type checks.

Example fix

// before
var obj = new OtherType();
serializer.Populate(reader, obj); // existingValue type != converter T

// after
var obj = new MyType(); // matches converter's T
serializer.Populate(reader, obj);
Defensive patterns

Strategy: validation

Validate before calling

if (existingValue != null && converter is JsonConverter<T> && existingValue is not T)
{
    existingValue = null; // or default(T)
}

Type guard

static bool ExistingValueCompatible<T>(object existing, JsonConverter<T> _)
    => existing is null or T;

Prevention

When it happens

Trigger: Deserialization with a JsonConverter<T> where the serializer passes an existingValue of a different type — e.g. populating an existing object whose declared type differs from T, or using JsonSerializer.Populate / a non-default current value with a converter that only accepts T. Also when CanConvert matches a type other than the one actually constructed for the existing value.

Common situations: Using Populate with converters; polymorphic deserialization where the runtime existing value's type does not match the converter's T; converters shared across a base type hierarchy.

Related errors


AI-assisted analysis of JamesNK/Newtonsoft.Json@4f73e74372 (2026-08-07). Data as JSON: /api/errors/cc765ca51301fd66. Report an issue: GitHub.