{"record":{"id":"8c01566d397646e1","repo":"JamesNK/Newtonsoft.Json","slug":"invalid-extension-data-attribute-on-0-member","errorCode":null,"errorMessage":"Invalid extension data attribute on '{0}'. Member '{1}' must have a getter.","messagePattern":"Invalid extension data attribute on '(.+?)'\\. Member '(.+?)' must have a getter\\.","errorType":"exception","errorClass":"JsonException","httpStatus":null,"severity":"error","filePath":"Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs","lineNumber":472,"sourceCode":"            });\n\n            MemberInfo? extensionDataMember = members.LastOrDefault(m =>\n            {\n                MemberTypes memberType = m.MemberType();\n                if (memberType != MemberTypes.Property && memberType != MemberTypes.Field)\n                {\n                    return false;\n                }\n\n                // last instance of attribute wins on type if there are multiple\n                if (!m.IsDefined(typeof(JsonExtensionDataAttribute), false))\n                {\n                    return false;\n                }\n\n                if (!ReflectionUtils.CanReadMemberValue(m, true))\n                {\n                    throw new JsonException(\"Invalid extension data attribute on '{0}'. Member '{1}' must have a getter.\".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(m.DeclaringType!), m.Name));\n                }\n\n                Type t = ReflectionUtils.GetMemberUnderlyingType(m);\n\n                if (ReflectionUtils.ImplementsGenericDefinition(t, typeof(IDictionary<,>), out Type? dictionaryType))\n                {\n                    Type keyType = dictionaryType.GetGenericArguments()[0];\n                    Type valueType = dictionaryType.GetGenericArguments()[1];\n\n                    if (keyType.IsAssignableFrom(typeof(string)) && valueType.IsAssignableFrom(typeof(JToken)))\n                    {\n                        return true;\n                    }\n                }\n\n                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));\n            });\n","sourceCodeStart":454,"sourceCodeEnd":490,"githubUrl":"https://github.com/JamesNK/Newtonsoft.Json/blob/4f73e74372445108d2c1bda37b36e6f5e43402e0/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs#L454-L490","documentation":"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.","triggerScenarios":"Applying [JsonExtensionData] to a property that only has a setter, e.g. 'public IDictionary<string, JToken> Extra { set; }'.","commonSituations":"Adding extension-data capture to a write-only property, or refactoring a property and accidentally dropping its getter while leaving the attribute in place.","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."],"exampleFix":"// before\n[JsonExtensionData]\npublic IDictionary<string, JToken> Extra { private set; } // no getter -> throws\n\n// after\n[JsonExtensionData]\npublic IDictionary<string, JToken> Extra { get; set; } = new Dictionary<string, JToken>();","handlingStrategy":"validation","validationCode":"// In a unit test, assert every [JsonExtensionData] member is readable.\nforeach (var m in typeof(T).GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))\n{\n    if (m.IsDefined(typeof(JsonExtensionDataAttribute), false))\n    {\n        if (!Newtonsoft.Json.Utilities.ReflectionUtils.CanReadMemberValue(m, true))\n            throw new InvalidOperationException($\"{m.Name} has [JsonExtensionData] but no getter.\");\n    }\n}","typeGuard":"static bool ExtensionDataMemberHasGetter(MemberInfo m)\n{\n    if (!m.IsDefined(typeof(JsonExtensionDataAttribute), false)) return true;\n    return Newtonsoft.Json.Utilities.ReflectionUtils.CanReadMemberValue(m, true);\n}","tryCatchPattern":"try { JsonConvert.DeserializeObject<T>(json); }\ncatch (JsonException ex) when (ex.Message.Contains(\"must have a getter\"))\n{\n    // add a getter to the flagged member\n}","preventionTips":["Always declare a getter on [JsonExtensionData] members.","Add a build-time reflection test that validates attribute usage on serializable types."],"tags":["extension-data","attribute-validation","contract-resolution"],"analyzedSha":"4f73e74372445108d2c1bda37b36e6f5e43402e0","analyzedAt":"2026-08-07T06:10:08.596Z","schemaVersion":2},"datasetVersion":"2026-08-07T07:17:06.508Z"}