JamesNK/Newtonsoft.Json · error · JsonException
Invalid extension data attribute on '{0}'. Member '{1}' type
Error message
Invalid extension data attribute on '{0}'. Member '{1}' type must implement IDictionary<string, JToken>. What it means
The [JsonExtensionData] member's type must implement IDictionary<string, JToken> (key assignable from string, value assignable from JToken). Contract resolution checks the generic dictionary arguments and throws a JsonException (line 488) if the member is any other collection/value type. Extension data is stored as JToken values keyed by string, so incompatible dictionary types are rejected.
Source
Thrown at Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs:488
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));
});
return extensionDataMember;
}
private static void SetExtensionDataDelegates(JsonObjectContract contract, MemberInfo member)
{
JsonExtensionDataAttribute? extensionDataAttribute = ReflectionUtils.GetAttribute<JsonExtensionDataAttribute>(member);
if (extensionDataAttribute == null)
{
return;
}
Type t = ReflectionUtils.GetMemberUnderlyingType(member);
ReflectionUtils.ImplementsGenericDefinition(t, typeof(IDictionary<,>), out Type? dictionaryType);
Type keyType = dictionaryType!.GetGenericArguments()[0];View on GitHub (pinned to 4f73e74372)
Solutions
- Change the member type to IDictionary<string, JToken> or Dictionary<string, JToken>.
- If you need strongly-typed values, deserialize into JToken and convert in an OnDeserialized callback rather than changing the dictionary type.
Example fix
// before
[JsonExtensionData]
public Dictionary<string, object> Extra { get; set; } // wrong value type
// after
[JsonExtensionData]
public Dictionary<string, JToken> Extra { get; set; } = new Dictionary<string, JToken>(); Defensive patterns
Strategy: validation
Validate before calling
foreach (var p in typeof(T).GetProperties().Where(p => p.IsDefined(typeof(JsonExtensionDataAttribute), false)))
{
var t = p.PropertyType;
var ok = t.GetInterfaces().Concat(new[] { t })
.Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<,>)
&& i.GetGenericArguments()[0] == typeof(string)
&& typeof(JToken).IsAssignableFrom(i.GetGenericArguments()[1]));
if (!ok) throw new InvalidOperationException($"{p.Name} must be IDictionary<string, JToken>.");
} Type guard
static bool IsValidExtensionDataType(Type t)
{
return t.GetInterfaces().Concat(new[] { t }).Any(i =>
i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<,>)
&& i.GetGenericArguments()[0] == typeof(string)
&& typeof(JToken).IsAssignableFrom(i.GetGenericArguments()[1]));
} Try / catch
try { JsonConvert.DeserializeObject<T>(json); }
catch (JsonException ex) when (ex.Message.Contains("IDictionary<string, JToken>"))
{
// change the member type to IDictionary<string, JToken>
} Prevention
- Use IDictionary<string, JToken> or Dictionary<string, JToken> for extension data.
- Avoid Dictionary<string, object>; it is not supported.
When it happens
Trigger: Decorating a member of the wrong type, e.g. [JsonExtensionData] Dictionary<string, object>, List<KeyValuePair<...>>, Dictionary<int, JToken>, or a non-dictionary field.
Common situations: Assuming [JsonExtensionData] accepts Dictionary<string, object> (it does not), or copy-pasting the attribute onto an unrelated collection member.
Related errors
- Invalid extension data attribute on '{0}'. Member '{1}' must
- 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/78e1975add315895.
Report an issue: GitHub.