JamesNK/Newtonsoft.Json · error · JsonException

Serialization Callback '{1}' in type '{0}' must have a singl

Error message

Serialization Callback '{1}' in type '{0}' must have a single parameter of type '{2}'.

What it means

Serialization lifecycle callbacks ([OnSerializing], [OnSerialized], [OnDeserializing], [OnDeserialized]) must take exactly one parameter of type StreamingContext. IsValidCallback checks the parameter count and type and throws a JsonException (line 1346) otherwise. The streaming context is the only argument these hooks accept.

Source

Thrown at Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs:1346

            }

            if (method.ReturnType != typeof(void))
            {
                throw new JsonException("Serialization Callback '{1}' in type '{0}' must return void.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(method.DeclaringType!), method));
            }

            if (attributeType == typeof(OnErrorAttribute))
            {
                if (parameters == null || parameters.Length != 2 || parameters[0].ParameterType != typeof(StreamingContext) || parameters[1].ParameterType != typeof(ErrorContext))
                {
                    throw new JsonException("Serialization Error Callback '{1}' in type '{0}' must have two parameters of type '{2}' and '{3}'.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(method.DeclaringType!), method, typeof(StreamingContext), typeof(ErrorContext)));
                }
            }
            else
            {
                if (parameters == null || parameters.Length != 1 || parameters[0].ParameterType != typeof(StreamingContext))
                {
                    throw new JsonException("Serialization Callback '{1}' in type '{0}' must have a single parameter of type '{2}'.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(method.DeclaringType!), method, typeof(StreamingContext)));
                }
            }

            prevAttributeType = attributeType;

            return true;
        }

        internal static string GetClrTypeFullName(Type type)
        {
            if (type.IsGenericTypeDefinition() || !type.ContainsGenericParameters())
            {
                return type.FullName!;
            }

            return "{0}.{1}".FormatWith(CultureInfo.InvariantCulture, type.Namespace, type.Name);
        }

View on GitHub (pinned to 4f73e74372)

Solutions

  1. Give the callback the signature 'void M(StreamingContext context)'.
  2. Access the object being serialized via 'this' inside an instance method rather than as a parameter.

Example fix

// before
public class Model {
    [OnDeserialized]
    internal void OnDeserialized() {} // missing parameter -> throws
}

// after
public class Model {
    [OnDeserialized]
    internal void OnDeserialized(StreamingContext context) {}
}
Defensive patterns

Strategy: validation

Validate before calling

var attrs = new[] { typeof(OnSerializingAttribute), typeof(OnSerializedAttribute), typeof(OnDeserializingAttribute), typeof(OnDeserializedAttribute) };
foreach (var m in typeof(T).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
    if (attrs.Any(a => m.IsDefined(a, false)))
    {
        var ps = m.GetParameters();
        if (ps.Length != 1 || ps[0].ParameterType != typeof(StreamingContext))
            throw new InvalidOperationException($"Callback {m.Name} must take a single StreamingContext.");
    }
}

Type guard

static bool LifecycleCallbackSignatureIsValid(MethodInfo m)
{
    var attrs = new[] { typeof(OnSerializingAttribute), typeof(OnSerializedAttribute), typeof(OnDeserializingAttribute), typeof(OnDeserializedAttribute) };
    if (!attrs.Any(a => m.IsDefined(a, false))) return true;
    var ps = m.GetParameters();
    return ps.Length == 1 && ps[0].ParameterType == typeof(StreamingContext);
}

Try / catch

try { JsonConvert.DeserializeObject<T>(json); }
catch (JsonException ex) when (ex.Message.Contains("must have a single parameter of type"))
{
    // add/fix the single StreamingContext parameter
}

Prevention

When it happens

Trigger: A callback method declared with no parameters, or with parameters of a type other than StreamingContext (e.g. the model type itself).

Common situations: Declaring a parameterless callback, or passing the deserialized object as a parameter by mistake.

Related errors


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