This is a follow on from #7985 which has been closed. After some deeper investigation I have found the root cause is when Application Insights Snapshot Debugger is enabled in Azure App Service or locally it adds a dictionary with an object
type key to the exception's Data
property which can't be serialized by the exception middleware:
-
ABP Framework version: v8.1.1
-
UI Type: Angular
-
Database System: EF Core (SQL Server)
-
Tiered (for MVC) or Auth Server Separated (for Angular): yes
-
Exception message and full stack trace:
System.NotSupportedException: The type 'System.Object' is not a supported dictionary key using converter of type 'System.Text.Json.Serialization.Converters.DefaultObjectConverter'. Path: $.Error.Data. ---> System.NotSupportedException: The type 'System.Object' is not a supported dictionary key using converter of type 'System.Text.Json.Serialization.Converters.DefaultObjectConverter'. at System.Text.Json.ThrowHelper.ThrowNotSupportedException_DictionaryKeyTypeNotSupported(Type keyType, JsonConverter converter) at System.Text.Json.Serialization.Converters.ObjectConverter.WriteAsPropertyNameCore(Utf8JsonWriter writer, Object value, JsonSerializerOptions options, Boolean isWritingExtensionDataProperty) at System.Text.Json.Serialization.JsonConverter
1.WriteAsPropertyName(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
at System.Text.Json.Serialization.JsonConverter1.WriteAsPropertyNameCore(Utf8JsonWriter writer, T value, JsonSerializerOptions options, Boolean isWritingExtensionDataProperty) at System.Text.Json.Serialization.Converters.IDictionaryConverter
1.OnWriteResume(Utf8JsonWriter writer, TDictionary value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonDictionaryConverter3.OnTryWrite(Utf8JsonWriter writer, TDictionary dictionary, JsonSerializerOptions options, WriteStack& state) at System.Text.Json.Serialization.JsonConverter
1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.Metadata.JsonPropertyInfo1.GetMemberAndWriteJson(Object obj, WriteStack& state, Utf8JsonWriter writer) at System.Text.Json.Serialization.Converters.ObjectDefaultConverter
1.OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonConverter1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state) at System.Text.Json.Serialization.Metadata.JsonPropertyInfo
1.GetMemberAndWriteJson(Object obj, WriteStack& state, Utf8JsonWriter writer)
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter1.OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, WriteStack& state) at System.Text.Json.Serialization.JsonConverter
1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonConverter1.WriteCore(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state) --- End of inner exception stack trace --- at System.Text.Json.ThrowHelper.ThrowNotSupportedException(WriteStack& state, NotSupportedException ex) at System.Text.Json.Serialization.JsonConverter
1.WriteCore(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo1.SerializeAsync(Stream utf8Json, T rootValue, CancellationToken cancellationToken, Object rootValueBoxed) at System.Text.Json.Serialization.Metadata.JsonTypeInfo
1.SerializeAsync(Stream utf8Json, T rootValue, CancellationToken cancellationToken, Object rootValueBoxed)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo1.SerializeAsync(Stream utf8Json, T rootValue, CancellationToken cancellationToken, Object rootValueBoxed) at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeResultAsync>g__Logged|22_0(ResourceInvoker invoker, IActionResult result) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAlwaysRunResultFilters>g__Awaited|27_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker) at Volo.Abp.AspNetCore.Serilog.AbpSerilogMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.InterfaceMiddlewareBinder.<>c__DisplayClass2_0.<<CreateMiddleware>b__0>d.MoveNext() --- End of stack trace from previous location --- at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Volo.Abp.AspNetCore.Security.Claims.AbpDynamicClaimsMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.InterfaceMiddlewareBinder.<>c__DisplayClass2_0.<<CreateMiddleware>b__0>d.MoveNext() --- End of stack trace from previous location --- at Volo.Abp.AspNetCore.Uow.AbpUnitOfWorkMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.InterfaceMiddlewareBinder.<>c__DisplayClass2_0.<<CreateMiddleware>b__0>d.MoveNext() --- End of stack trace from previous location --- at Volo.Abp.AspNetCore.ExceptionHandling.AbpExceptionHandlingMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Volo.Abp.AspNetCore.ExceptionHandling.AbpExceptionHandlingMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.InterfaceMiddlewareBinder.<>c__DisplayClass2_0.<<CreateMiddleware>b__0>d.MoveNext() --- End of stack trace from previous location --- at Volo.Abp.AspNetCore.MultiTenancy.MultiTenancyMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.InterfaceMiddlewareBinder.<>c__DisplayClass2_0.<<CreateMiddleware>b__0>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Builder.ApplicationBuilderAbpOpenIddictMiddlewareExtension.<>c__DisplayClass0_0.<<UseAbpOpenIddictValidation>b__0>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Volo.Abp.AspNetCore.Tracing.AbpCorrelationIdMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.InterfaceMiddlewareBinder.<>c__DisplayClass2_0.<<CreateMiddleware>b__0>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.RequestLocalization.AbpRequestLocalizationMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.InterfaceMiddlewareBinder.<>c__DisplayClass2_0.<<CreateMiddleware>b__0>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
-
Steps to reproduce the issue:
-
Enable Application Insights Snapshot Debugger
-
Throw a
UserFriendlyException
5 Answer(s)
-
0
Hi,
We will check it
-
0
Hi,
I can't reproduce the problem
public class TestAppService : Test8688AppService { public string Test() { var ex = new UserFriendlyException("test"); ex.Data["Test"] = new object(); JsonSerializer.Serialize(ex); throw ex; } }
-
0
Try setting the dictionary key to ‘new object()’ just like the snapshot debugger is doing. Example:
var ex = new UserFriendlyException("UserFriendlyException on purpose"); ex.Data[new object()] = "foobar";
-
0
Workaround that seems to resolve the issue
[Dependency(ReplaceServices = true)] public class CustomExceptionToErrorInfoConverter : DefaultExceptionToErrorInfoConverter { public CustomExceptionToErrorInfoConverter( IOptions localizationOptions, IStringLocalizerFactory stringLocalizerFactory, IStringLocalizer stringLocalizer, IServiceProvider serviceProvider) : base(localizationOptions, stringLocalizerFactory, stringLocalizer, serviceProvider) { } protected override RemoteServiceErrorInfo CreateErrorInfoWithoutCode(Exception exception, AbpExceptionHandlingOptions options) { var result = base.CreateErrorInfoWithoutCode(exception, options); MakeSerializationSafe(result); return result; } protected virtual void MakeSerializationSafe(RemoteServiceErrorInfo info) { var oldData = info.Data; // Create new dictionary so we're not attached to original Exception.Data which // can be manipulated by Application Insights Snapshot Debugger info.Data = new Dictionary(); // Copy any existing values, converting object keys to string if (oldData != null) { foreach (var key in oldData.Keys) { var newKey = key; if (key.GetType() == typeof(object)) { // Key is of type object which can't be serialized // Convert to string newKey = key.ToString(); if (info.Data.Contains(newKey)) { // Dictionary already contains this new key, appending Guid to make it unique newKey = $"{newKey}_{Guid.NewGuid()}"; } } info.Data[newKey] = oldData[key]; } } } }
-
0
Hi,
Try setting the dictionary key to ‘new object()’ just like the snapshot debugger is doing. Example:
I will check if it is a bug of ABP.