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

  1. Add a private setter to the property (`public string Name { get; private set; }`) — GetSetMethod(true) finds non-public setters and the error disappears.
  2. Annotate the read-only property with [JsonIgnore] so the serializer skips it entirely.
  3. Use a custom IContractResolver (e.g. derive from DefaultContractResolver) that filters out read-only properties in CreateProperties.
  4. 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

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


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