You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
NewtonsoftJsonSerializationProvider writes object graphs deeper than 64 levels that it then can't read back: Serialize succeeds, Deserialize returns null #155
NewtonsoftJsonSerializationProvider (Essentials.SerializationProviders.NewtonsoftJson/NewtonsoftJsonSerializationProvider.cs:21) uses a default JsonSerializerSettings. In Newtonsoft.Json 13, MaxDepth defaults to 64 and is enforced only when reading.
TrySerialize writes a graph of any depth and returns true.
Deserialize<T> on that same output throws JsonReaderException, which the provider catches and turns into default.
The provider can therefore produce data that it cannot read.
A linked chain class Node { string Name; Node? Next; }, round-tripped with Serialize and then Deserialize<Node>:
Depth
Serialize
Deserialize
64
ok
ok
65
ok
null
200
ok
null
With a file-backed persistence provider using Newtonsoft, StoreAsync reports success but RetrieveAsync returns null. RetrieveOrCreateAsync would then overwrite the stored data with a fresh instance (see #82).
JsonSerializationProvider (System.Text.Json) handles this consistently: for the 200-deep graph, TrySerialize returns false.
Suggested fix
Make writing and reading enforce the same limit. Either:
have TrySerialize fail when the output would go deeper than settings.MaxDepth, for example with a JsonTextWriter subclass that checks depth in WriteStartObject/WriteStartArray; or
set one explicit MaxDepth (including null for unlimited) that applies to both directions.
Combine this with the buffering fix in the companion issue about TrySerialize leaving truncated output, so a depth failure also leaves the writer empty.
Acceptance criteria
Any graph TrySerialize accepts always deserializes back.
A test round-trips a 65-deep chain: either both directions succeed, or TrySerialize returns false.
Area / suggested assignee:Essentials.SerializationProviders.NewtonsoftJson/NewtonsoftJsonSerializationProvider.cs (settings at L21, TrySerialize). Owner: @matt-edmondson
Next step: Enforce one MaxDepth on both write and read, using a depth-checking JsonTextWriter or a single explicit setting. Then add the 65-deep chain round-trip test.
Activity
matt-edmondson commentedon Oct 19, 2026
Triage
RetrieveOrCreateAsync(RetrieveOrCreateAsync overwrites a stored file it cannot parse with new T(), destroying the user's data #82), that leads to silent data loss.Essentials.SerializationProviders.NewtonsoftJson/NewtonsoftJsonSerializationProvider.cs(settings at L21,TrySerialize). Owner: @matt-edmondsonTrySerializeleaves truncated JSON in the writer on failure), and it compounds RetrieveOrCreateAsync overwrites a stored file it cannot parse with new T(), destroying the user's data #82. Land NewtonsoftJsonSerializationProvider.TrySerialize returns false but leaves well-formed, truncated JSON in the caller's writer #154's buffering first, or together with this, so a depth failure also leaves the writer empty. No open PR covers it.MaxDepthon both write and read, using a depth-checkingJsonTextWriteror a single explicit setting. Then add the 65-deep chain round-trip test.Generated by Claude Code