JamesNK/Newtonsoft.Json · error · ArgumentException
Property does not have a setter.
Error message
Property does not have a setter.
What it means
Thrown by ExpressionReflectionDelegateFactory.CreateSet(PropertyInfo) when propertyInfo.GetSetMethod(true) returns null, i.e. the property has no set accessor at all (public or private). The Expression factory is used for reference types; structs are routed to the LateBound factory instead, so this path only fires on classes. It surfaces during deserialization when Json.NET attempts to build a setter delegate to populate a read-only property.
Source
Thrown at Src/Newtonsoft.Json/Utilities/ExpressionReflectionDelegateFactory.cs:348
// use reflection for structs
// expression doesn't correctly set value
if (propertyInfo.DeclaringType!.IsValueType())
{
return LateBoundReflectionDelegateFactory.Instance.CreateSet<T>(propertyInfo);
}
Type instanceType = typeof(T);
Type valueType = typeof(object);
ParameterExpression instanceParameter = Expression.Parameter(instanceType, "instance");
ParameterExpression valueParameter = Expression.Parameter(valueType, "value");
Expression readValueParameter = EnsureCastExpression(valueParameter, propertyInfo.PropertyType);
MethodInfo? setMethod = propertyInfo.GetSetMethod(true);
if (setMethod == null)
{
throw new ArgumentException("Property does not have a setter.");
}
Expression setExpression;
if (setMethod.IsStatic)
{
setExpression = Expression.Call(setMethod, readValueParameter);
}
else
{
Expression readInstanceParameter = EnsureCastExpression(instanceParameter, propertyInfo.DeclaringType!);
setExpression = Expression.Call(readInstanceParameter, setMethod, readValueParameter);
}
LambdaExpression lambdaExpression = Expression.Lambda(typeof(Action<T, object?>), setExpression, instanceParameter, valueParameter);
Action<T, object?> compiled = (Action<T, object?>)lambdaExpression.Compile();
return compiled;View on GitHub (pinned to 4f73e74372)
Solutions
- Add a private setter to the property (`public string Name { get; private set; }`) — GetSetMethod(true) finds non-public setters and the error disappears.
- Annotate the read-only property with [JsonIgnore] so the serializer skips it entirely.
- Use a custom IContractResolver (e.g. derive from DefaultContractResolver) that filters out read-only properties in CreateProperties.
- Use [OnDeserialized] / constructor deserialization with [JsonConstructor] to populate the field instead of the property.
Example fix
// before
public string Name { get; }
// after
public string Name { get; private set; } Defensive patterns
Strategy: validation
Validate before calling
// Before handing a PropertyInfo to a setter pipeline, ensure it is settable. bool CanSet(PropertyInfo p) => p.GetSetMethod(true) != null;
Type guard
static bool IsSettable(PropertyInfo p) => p.GetSetMethod(true) != null;
Prevention
- Default to private setters on DTO properties so serialization can populate them.
- Use a custom IContractResolver that omits read-only properties from the contract.
- Run a unit test that round-trips each DTO to catch read-only property issues early.
When it happens
Trigger: Deserializing JSON into a reference type whose property is declared get-only (e.g. `public string Name { get; }`), where the contract resolver selects that property for population. Also any direct call to CreateSet<T>(PropertyInfo) on a PropertyInfo returned by GetSetMethod(true) == null.
Common situations: Immutable DTOs or records using get-only auto-properties, computed read-only properties, properties whose backing field is only set in a constructor, or a refactor that removed a setter. Also migrating to C# 6+ get-only auto-properties without updating serialization.
Related errors
- Error setting value to '{0}' on '{1}'.
- Error setting value to '{0}' on '{1}'.
- Error setting value to '{0}' on '{1}'.
- Property '{0}' does not have a getter.
- Property does not have a getter.
AI-assisted analysis of JamesNK/Newtonsoft.Json@4f73e74372 (2026-08-07).
Data as JSON: /api/errors/f6a3c30ff480e541.
Report an issue: GitHub.