JamesNK/Newtonsoft.Json · error · JsonException
Invalid extension data attribute on '{0}'. Member '{1}' must
Error message
Invalid extension data attribute on '{0}'. Member '{1}' must have a getter. What it means
During contract resolution, GetExtensionDataMemberForType verifies that any member decorated with [JsonExtensionData] is readable (ReflectionUtils.CanReadMemberValue). If the member has no getter (a write-only property or an unsettable field), a JsonException is thrown (line 472). Extension data capture needs to read the dictionary back out, so a get accessor is mandatory.
Source
Thrown at Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs:472
});
MemberInfo? extensionDataMember = members.LastOrDefault(m =>
{
MemberTypes memberType = m.MemberType();
if (memberType != MemberTypes.Property && memberType != MemberTypes.Field)
{
return false;
}
// last instance of attribute wins on type if there are multiple
if (!m.IsDefined(typeof(JsonExtensionDataAttribute), false))
{
return false;
}
if (!ReflectionUtils.CanReadMemberValue(m, true))
{
throw new JsonException("Invalid extension data attribute on '{0}'. Member '{1}' must have a getter.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(m.DeclaringType!), m.Name));
}
Type t = ReflectionUtils.GetMemberUnderlyingType(m);
if (ReflectionUtils.ImplementsGenericDefinition(t, typeof(IDictionary<,>), out Type? dictionaryType))
{
Type keyType = dictionaryType.GetGenericArguments()[0];
Type valueType = dictionaryType.GetGenericArguments()[1];
if (keyType.IsAssignableFrom(typeof(string)) && valueType.IsAssignableFrom(typeof(JToken)))
{
return true;
}
}
throw new JsonException("Invalid extension data attribute on '{0}'. Member '{1}' type must implement IDictionary<string, JToken>.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(m.DeclaringType!), m.Name));
});
View on GitHub (pinned to 4f73e74372)
Solutions
- Add a getter to the property so the extension-data dictionary can be read.
- If the member must remain write-only, remove [JsonExtensionData] and handle extra properties another way.
Example fix
// before
[JsonExtensionData]
public IDictionary<string, JToken> Extra { private set; } // no getter -> throws
// after
[JsonExtensionData]
public IDictionary<string, JToken> Extra { get; set; } = new Dictionary<string, JToken>(); Defensive patterns
Strategy: validation
Validate before calling
// In a unit test, assert every [JsonExtensionData] member is readable.
foreach (var m in typeof(T).GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
if (m.IsDefined(typeof(JsonExtensionDataAttribute), false))
{
if (!Newtonsoft.Json.Utilities.ReflectionUtils.CanReadMemberValue(m, true))
throw new InvalidOperationException($"{m.Name} has [JsonExtensionData] but no getter.");
}
} Type guard
static bool ExtensionDataMemberHasGetter(MemberInfo m)
{
if (!m.IsDefined(typeof(JsonExtensionDataAttribute), false)) return true;
return Newtonsoft.Json.Utilities.ReflectionUtils.CanReadMemberValue(m, true);
} Try / catch
try { JsonConvert.DeserializeObject<T>(json); }
catch (JsonException ex) when (ex.Message.Contains("must have a getter"))
{
// add a getter to the flagged member
} Prevention
- Always declare a getter on [JsonExtensionData] members.
- Add a build-time reflection test that validates attribute usage on serializable types.
When it happens
Trigger: Applying [JsonExtensionData] to a property that only has a setter, e.g. 'public IDictionary<string, JToken> Extra { set; }'.
Common situations: Adding extension-data capture to a write-only property, or refactoring a property and accidentally dropping its getter while leaving the attribute in place.
Related errors
- Invalid extension data attribute on '{0}'. Member '{1}' type
- Multiple constructors with the JsonConstructorAttribute.
- Constructor for '{0}' must have no parameters or a single pa
- Invalid attribute. Both '{0}' and '{1}' in type '{2}' have '
- Invalid Callback. Method '{3}' in type '{2}' has both '{0}'
AI-assisted analysis of JamesNK/Newtonsoft.Json@4f73e74372 (2026-08-07).
Data as JSON: /api/errors/8c01566d397646e1.
Report an issue: GitHub.