{"record":{"id":"a45c6a17b86eacf2","repo":"JamesNK/Newtonsoft.Json","slug":"invalid-attribute-both-0-and-1-in-type","errorCode":null,"errorMessage":"Invalid attribute. Both '{0}' and '{1}' in type '{2}' have '{3}'.","messagePattern":"Invalid attribute\\. Both '(.+?)' and '(.+?)' in type '(.+?)' have '(.+?)'\\.","errorType":"exception","errorClass":"JsonException","httpStatus":null,"severity":"error","filePath":"Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs","lineNumber":1317,"sourceCode":"            if (type == typeof(DateOnly) || type == typeof(TimeOnly))\n            {\n                return true;\n            }\n#endif\n\n            return false;\n        }\n\n        private static bool IsValidCallback(MethodInfo method, ParameterInfo[] parameters, Type attributeType, MethodInfo? currentCallback, ref Type? prevAttributeType)\n        {\n            if (!method.IsDefined(attributeType, false))\n            {\n                return false;\n            }\n\n            if (currentCallback != null)\n            {\n                throw new JsonException(\"Invalid attribute. Both '{0}' and '{1}' in type '{2}' have '{3}'.\".FormatWith(CultureInfo.InvariantCulture, method, currentCallback, GetClrTypeFullName(method.DeclaringType!), attributeType));\n            }\n\n            if (prevAttributeType != null)\n            {\n                throw new JsonException(\"Invalid Callback. Method '{3}' in type '{2}' has both '{0}' and '{1}'.\".FormatWith(CultureInfo.InvariantCulture, prevAttributeType, attributeType, GetClrTypeFullName(method.DeclaringType!), method));\n            }\n\n            if (method.IsVirtual)\n            {\n                throw new JsonException(\"Virtual Method '{0}' of type '{1}' cannot be marked with '{2}' attribute.\".FormatWith(CultureInfo.InvariantCulture, method, GetClrTypeFullName(method.DeclaringType!), attributeType));\n            }\n\n            if (method.ReturnType != typeof(void))\n            {\n                throw new JsonException(\"Serialization Callback '{1}' in type '{0}' must return void.\".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(method.DeclaringType!), method));\n            }\n\n            if (attributeType == typeof(OnErrorAttribute))","sourceCodeStart":1299,"sourceCodeEnd":1335,"githubUrl":"https://github.com/JamesNK/Newtonsoft.Json/blob/4f73e74372445108d2c1bda37b36e6f5e43402e0/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs#L1299-L1335","documentation":"Each serialization callback attribute ([OnSerializing], [OnSerialized], [OnDeserializing], [OnDeserialized], [OnError]) may be applied to at most one method per type. IsValidCallback tracks the currently registered callback for an attribute and throws a JsonException (line 1317) if a second method with the same attribute is encountered. An unambiguous single callback is required per lifecycle event.","triggerScenarios":"Two methods in the same class both decorated with the same callback attribute, e.g. two [OnDeserialized] methods.","commonSituations":"Copy-pasting a callback method and forgetting to remove the original, or merging types during refactoring so a base and derived method both carry the same attribute.","solutions":["Consolidate the two methods into a single method carrying the attribute.","Remove the attribute from one of the methods."],"exampleFix":"// before\npublic class Model {\n    [OnDeserialized] internal void OnDeserializedA(StreamingContext c) {}\n    [OnDeserialized] internal void OnDeserializedB(StreamingContext c) {} // throws\n}\n\n// after\npublic class Model {\n    [OnDeserialized] internal void OnDeserialized(StreamingContext c) {}\n}","handlingStrategy":"validation","validationCode":"var attrs = new[] { typeof(OnSerializingAttribute), typeof(OnSerializedAttribute), typeof(OnDeserializingAttribute), typeof(OnDeserializedAttribute), typeof(OnErrorAttribute) };\nforeach (var a in attrs)\n{\n    var methods = typeof(T).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)\n        .Where(m => m.IsDefined(a, false)).ToList();\n    if (methods.Count > 1)\n        throw new InvalidOperationException($\"{methods.Count} methods on {typeof(T)} carry {a.Name}; only one allowed.\");\n}","typeGuard":"static bool HasAtMostOneCallbackPerAttribute(Type t)\n{\n    var attrs = new[] { typeof(OnSerializingAttribute), typeof(OnSerializedAttribute), typeof(OnDeserializingAttribute), typeof(OnDeserializedAttribute), typeof(OnErrorAttribute) };\n    return attrs.All(a => t.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)\n        .Count(m => m.IsDefined(a, false)) <= 1);\n}","tryCatchPattern":"try { JsonConvert.DeserializeObject<T>(json); }\ncatch (JsonException ex) when (ex.Message.Contains(\"Both\") && ex.Message.Contains(\"have\"))\n{\n    // remove the duplicate callback attribute\n}","preventionTips":["Apply each On* attribute to exactly one method per type.","Add a reflection test that asserts single-callback-per-attribute invariant."],"tags":["callback","attribute-validation","contract-resolution"],"analyzedSha":"4f73e74372445108d2c1bda37b36e6f5e43402e0","analyzedAt":"2026-08-07T06:10:08.596Z","schemaVersion":2},"datasetVersion":"2026-08-07T07:17:06.508Z"}