JamesNK/Newtonsoft.Json · error · JsonSerializationException

Converter cannot write specified value to JSON. {0} is requi

Error message

Converter cannot write specified value to JSON. {0} is required.

What it means

Thrown by the sealed override JsonConverter<T>.WriteJson when the value handed to the converter is incompatible with the converter's generic type T. The base sealed method guards before delegating to the user-implemented WriteJson(writer, T?, serializer): if the value is non-null and not assignable to T (or null when T is not nullable), serialization is aborted with a JsonSerializationException naming the required type T. This protects the typed WriteJson from an invalid downcast.

Source

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

    }

    /// <summary>
    /// Converts an object to and from JSON.
    /// </summary>
    /// <typeparam name="T">The object type to convert.</typeparam>
    public abstract class JsonConverter<T> : JsonConverter
    {
        /// <summary>
        /// Writes the JSON representation of the object.
        /// </summary>
        /// <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 sealed override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
        {
            if (!(value != null ? value is T : ReflectionUtils.IsNullable(typeof(T))))
            {
                throw new JsonSerializationException("Converter cannot write specified value to JSON. {0} is required.".FormatWith(CultureInfo.InvariantCulture, typeof(T)));
            }
            WriteJson(writer, (T?)value, serializer);
        }

        /// <summary>
        /// Writes the JSON representation of the object.
        /// </summary>
        /// <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>

View on GitHub (pinned to 4f73e74372)

Solutions

  1. Make CanConvert return true only for T (and Nullable<T> if intended) so the converter is never selected for other types.
  2. Scope the converter with [JsonConverter(typeof(MyConverter<T>))] on the exact property/type instead of adding it to settings.Converters.
  3. If you must handle null, ensure T is nullable (class or Nullable<T>) or override WriteJson to short-circuit nulls before the base guard.

Example fix

// before
public override bool CanConvert(Type objectType) => true; // matches everything

// after
public override bool CanConvert(Type objectType)
    => objectType == typeof(MyType) || Nullable.GetUnderlyingType(objectType) == typeof(MyType);
Defensive patterns

Strategy: validation

Validate before calling

var converters = settings.Converters;
foreach (var c in converters)
{
    if (value != null && !c.CanConvert(value.GetType()))
    {
        // do not let this converter handle value
    }
}

Type guard

static bool CanConvertStrict<T>(JsonConverter<T> c, Type objectType)
    where T : class
    => objectType == typeof(T) || Nullable.GetUnderlyingType(objectType) == typeof(T);

Prevention

When it happens

Trigger: A JsonConverter<T> is registered (via attributes or settings.Converters) but the serializer offers it a value whose runtime type is not T (or a nullable T when null is passed). Common when a converter is applied too broadly via JsonSerializerSettings.Converters and gets invoked for unrelated types, or when CanConvert returns true for types the converter cannot actually write.

Common situations: Registering a single generic converter globally for many types; a polymorphic property whose declared type differs from T; CanConvert over-matching (returning true for base types or interfaces); passing null to a converter whose T is a non-nullable value type.

Related errors


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