Skip to content

Thread safety issue in STJ.JsonObject #120617

@eangelin

Description

@eangelin

Description

Calling GetPath on a JsonObject child node in parallel triggers InvalidOperationException.

Reproduction Steps

var tree = (JsonNode.Parse(
    """
    {
      "oh": "noes"
    }
    """
) as JsonObject)!;

// Workaround, do this once before parallel
// tree.First().Value!.GetPath();

Parallel.ForEach(Enumerable.Range(0, 10), _ =>
{
    tree.First().Value!.GetPath();
});

Expected behavior

I expect parallel reads from the JsonNode/Object/Values to be thread safe.

Actual behavior

On JsonObject.GetPath, line string propertyName = FindValue(child)!.Value.Key; I get the following exception + stack trace:

System.InvalidOperationException: Nullable object must have a value.
   at System.Nullable`1.get_Value()
   at System.Text.Json.Nodes.JsonObject.GetPath(ValueStringBuilder& path, JsonNode child)
   at System.Text.Json.Nodes.JsonNode.GetPath()
   ...

Regression?

Possibly regression from #77421

Known Workarounds

Traverse the object hierarchy serially once before parallel access to force lazy-initialization.

Configuration

Using .net sdk 9.0.305.

I don't think the rest matters, but macOS 15.7.1, arm64.

Other information

I guess calling tree.First() in the repro triggers initialization of the underlying Dictionary and I can't see anything in place to avoid a race condition there.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions