umbraco / Umbraco.Deploy.Issues

1 stars 0 forks source link

Content compare fails after Json deserialization error in the backend #212

Closed tormnator closed 5 months ago

tormnator commented 5 months ago

The content compare feature currently fails on many of our pages due to an exception being thrown on the backend while deserializing the json content returned from the remote environment. I have tried to debug and identify exactly why it happens, but had to give up due to time constraints. What's clear is that the deserializer expect an 'id' property on the json object, and that this property is either missing or equal to '0' (I'm not sure about the '0'). I've been unable to determine why the property is missing, but it seems to be the case on many of our content pages. One clue might be that the property is on a content composition, but I'm not sure if this is always the case.

I have tried to make changes to the offending property on one of the pages, including making minor changes to the doc type itself and resaving, with the hope that this would output the missing 'id' property value, but with no luck.

So, at the end, I'm not sure if this is a code issue in Umbraco.Deploy or if it's a data issue (possibly caused by another code issue).

I'm on Umbraco 12.3.7 with Umbraco.Deploy.Cloud 12.1.4.

I can't say what steps to take to reproduce the error since I don't know why the 'id' property is missing (or if that is the actual problem), but given that the issue is there, right click on the content node in the content tree and choose "Compare" in the menu. You will now see the Compare dialog with three animated bullets. The animation never ends due to the error. Open the browser's DevTools and you should see an error similar to the screenshot below in the console:

image

Here's the full formatted error information:

{
  "Type": "Newtonsoft.Json.JsonSerializationException",
  "Title": "An error has occurred.",
  "Status": 500,
  "Detail": "Required property 'id' not found in JSON. Path 'variantDisplay.tabs[1].properties[1]', line 1, position 4257.",
  "Instance": null,
  "Extensions": {
    "StackTrace": "...(see below)"
  }
}

at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EndProcessProperty(Object newObject, JsonReader reader, JsonObjectContract contract, Int32 initialDepth, JsonProperty property, PropertyPresence presence, Boolean setDefaultValue)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateList(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, String id)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateList(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, String id)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)\r\n  
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)\r\n  
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)\r\n  
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)\r\n  
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)\r\n  
at Umbraco.Deploy.Infrastructure.Extensions.HttpContentExtensions.ReadAsAsync[T](HttpContent content, JsonSerializerSettings settings, CancellationToken token)\r\n  
at Umbraco.Deploy.Infrastructure.Environments.RemoteUmbracoEnvironment.GetContentSummaryModel(Guid key, String culture, CancellationToken token)\r\n  
at Umbraco.Deploy.UI.Controllers.UiControllerBase.GetContentComparison(GetComparisonModel model)\r\n  
at lambda_method24308.lambda_method24308(Closure undefined, Object undefined)\r\n  
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)\r\n  
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)\r\n  
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\r\n  
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)\r\n  
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)\r\n  
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\r\n  
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
ronaldbarendse commented 5 months ago

Hi @tormnator, thanks for reporting this issue! I've been able to reproduce this on all latest Deploy minors (4.9, 10.3, 12.1 and 13.0) and can confirm this is a regression caused by using DefaultValueHandling.Ignore for our JSON API responses.

We initially added this JSON serialization setting to reduce the artifact size of exports, but these settings are also used for JSON API responses (used when transferring, restoring and comparing between environments). And because the CMS adds explicit attributes on the property ID to require it to be present, this caused the reported exception.

We've fixed this by using slightly different JSON settings for the API responses, so we can keep the exported artifacts unchanged (and as small as possible). This fix will be available in the next minors (4.10, 10.4, 12.2 and 13.1) that will be released Tuesday next week.

tormnator commented 5 months ago

Good job @ronaldbarendse identifying the problem and fixing it quickly! 👍