dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.39k stars 4.75k forks source link

Reading JsonNode Property Throws 'Nullable object must have a value' on first access #106298

Closed terryaney closed 3 months ago

terryaney commented 3 months ago

Description

I have a JsonNode object with following content:

{
    "key": "DB_pmtforms_glossary_2K",
    "en": "\\u003Cstrong\\u003EWhat is a 50% Joint \\u0026 Survivor Annuity with 10-Year Certain and Life Annuity?\\u003C/strong\\u003E\\u003Cp\\u003EThis payment option provides a lifelong monthly benefit paid to you for your lifetime. If you die within ten years from the time these monthly payments start, your spouse receives the same amount of money each month for the balance of the ten-year guaranteed period. At the end of the ten years, payments to your spouse will continue at 50% of your benefit until the death of your spouse. If both you and your spouse die within ten years from the time these monthly payments start, your spouse\\u0027s beneficiary receives a lump sum distribution based on the value of the remaining benefits or can elect to receive the same amount of money each month for the balance of the ten- year guaranteed period.\\u003C/p\\u003E",
    "es": ""
}

At random times, when I try to read the key property, an InvalidOperationException is throw, but if I immediately retry reading it, it works.

Reproduction Steps

string? value = null;
try
{
    value = source[ cell.Name ]?.ToString() ?? ( historyIdCols.Contains( cell.Name ) ? (string?)source[ "index" ] : null );
}
catch ( Exception ex )
{
    value = source[ cell.Name ]?.ToString() ?? ( historyIdCols.Contains( cell.Name ) ? (string?)source[ "index" ] : null );
}

With following 'locals': source = JsonNode above cell.Name = "key" var historyIdCols = new[] { "id", "hisIndex" };

As mentioned, in the try {} that randomly fails with:

System.InvalidOperationException Exception, Depth 1, information: Nullable object must have a value.

Here is complete exception stack:

System.InvalidOperationException Exception, Depth 1, information: Nullable object must have a value.

   at System.Nullable`1.get_Value()
   at System.Text.Json.Nodes.JsonObject.InitializeIfRequired()
   at System.Text.Json.Nodes.JsonObject.System.Collections.Generic.IDictionary<System.String,System.Text.Json.Nodes.JsonNode>.TryGetValue(String propertyName, JsonNode& jsonNode)
   at System.Text.Json.Nodes.JsonObject.GetItem(String propertyName)
   at KAT.Camelot.RBLe.Core.Calculations.CalculationService`4.LoadTableRow(CalcEngineTable table, JsonNode source, TRange firstRow) in D:\tfs.agent\_work\19\s\.repositories\Camelot\RBLe\Core\src\Calculations\CalculationService\CalculationService.cs:line 448

I'm trying to figure out if there is anything I can change in my code to eliminate this exception pattern.

Expected behavior

I wouldn't expect the first attempt to throw an exception when the same exact re-try attempt works.

Actual behavior

First attempt throws an exception when the same exact re-try attempt does not.

Regression?

No response

Known Workarounds

Retry the property get statement again.

Configuration

.NET7, x64, Windows 11 Pro.

Other information

No response

dotnet-policy-service[bot] commented 3 months ago

Tagging subscribers to this area: @dotnet/area-system-text-json, @gregsdennis See info in area-owners.md if you want to be subscribed.

eiriktsarpalis commented 3 months ago

Duplicate of #77421. It was fixed in .NET 8 and the System.Text.Json 8.0.0 NuGet package. I would recommend updating to that as 7.0.x is out of support and has known vulnerabilities (unrelated to this particular issue).

terryaney commented 3 months ago

Duplicate of #77421. It was fixed in .NET 8 and the System.Text.Json 8.0.0 NuGet package. I would recommend updating to that as 7.0.x is out of support and has known vulnerabilities (unrelated to this particular issue).

@eiriktsarpalis Curious, are you sure that is correct issue that was linked? That seems to focus more on thread safety, but maybe my issue was also resolved in that code.

eiriktsarpalis commented 3 months ago

Curious, are you sure that is correct issue that was linked?

Does the issue repro for you using STJ v8?

That seems to focus more on thread safety, but maybe my issue was also resolved in that code.

You mention that the issue happens randomly, which has the hallmarks of a race condition occurring. Your stacktrace stems from InitializeIfRequired which was precisely the source of the race condition that got fixed.

terryaney commented 3 months ago

Ah, ok then. And no, I haven't upgrade to .net8 yet. I tried when it came out and had some issues with web apis and I think something with Json as well. I took notes, but I'll give it another go in the near future. Thanks.