Assignment in Getter in Newtonsoft.Json

JamesNK/Newtonsoft.JsonPR#1950 ↗
GCI0036GCI0004BLOCKAPI ContractsBehavioral Correctness

Context

Newtonsoft.Json PR#1950 modified XmlNodeConverter.cs. It introduced a property getter that also performs a write - assigning a value inside what should be a pure read operation. Property getters are expected to be side-effect-free. When a getter mutates state, callers using reflection, lazy initialization, caching layers, or serialization frameworks may observe inconsistent behavior because they assume reads are safe to repeat without side effects.

Diff evidence

src/Newtonsoft.Json/Converters/XmlNodeConverter.cs
// Added in XmlNodeConverter.cs
+public string NodeType
+{
+ get
+ {
+ _nodeType = GetCurrentNodeType(); // GCI0036: mutation in getter
+ return _nodeType;
+ }
+}
[GCI0036] Pure Context Mutation
Location : src/Newtonsoft.Json/Converters/XmlNodeConverter.cs
Summary  : Assignment in getter - mutation in a pure context.
Evidence : _nodeType = GetCurrentNodeType();
Why      : Property getters are expected to be side-effect free. Mutations break
           this contract and can cause subtle bugs with lazy initialization,
           caching, or framework reflection.
Action   : Move state mutations to setter, constructor, or a dedicated method.

Why it matters

Newtonsoft.Json is the most downloaded NuGet package ever, with well over 3 billion downloads. Any serialization framework, ORM, or reflection-based tool that reads this property twice may see different values if the underlying state changes between reads. JSON.NET itself reads node properties repeatedly during traversal. A getter that writes state creates a timing dependency between reads - a class of bug that is nearly impossible to reproduce in unit tests (which call the getter once in a controlled sequence) but surfaces intermittently in production under concurrent serialization workloads.

Detection rules