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

  1. Add a getter to the property so the extension-data dictionary can be read.
  2. 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

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


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