MessagePack-CSharp/MessagePack-CSharp

Null value received in a custom formatter for Exception

Opened this issue · 3 comments

Bug description

When trying to serialize/deserialize an exception object using TypelessContractlessStandardResolver, some details like StackTrace and Source are getting lost on the deserialized object.
Hence, I tried to write a custom IMessagePackFormatter. There are two scenarios now :

  1. Serialize an exception with no inner exception

    The value parameter received in Serialize method is always null

  2. Serialize an exception with inner exception

    The value parameter received in Serialize method is the inner exception and not the actual exception that was passed for serialization

As a result, I am not able to get a workaround even with a custom IMessagePackFormatter.

Repro steps

  • Add a custom IMessagePackFormatter
  internal class ExceptionFormatter : IMessagePackFormatter<Exception>
 {
    public Exception Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
    {

    }

    public void Serialize(ref MessagePackWriter writer, Exception value, MessagePackSerializerOptions options)
    {
        /// value is null here if exception has no inner exception else inner exception instead of actual exception passed for serialization
    }
 }
  • Configure a custom resolver to use the custom ExceptionFormatter

interface ISerializer
{
    byte[] Serialize(object obj);

    T Deserialize<T>(byte[] bytes);
}

public class MessagePackTypeLessSerializer : ISerializer
{
    private static MessagePackSerializerOptions serializerOptions;
    static MessagePackTypeLessSerializer()
    {
        var resolver = CompositeResolver.Create(
               [new ExceptionFormatter()],
               [TypelessContractlessStandardResolver.Instance]);
        serializerOptions = MessagePackSerializerOptions.Standard.WithResolver(resolver); ;
    }

    public byte[] Serialize(object obj)
    {      
        return MessagePackSerializer.Typeless.Serialize(obj, serializerOptions);
    }

    public T Deserialize<T>(byte[] bytes)
    {
        if (MessagePackSerializer.Typeless.Deserialize(bytes, serializerOptions) is T result)
        {
            return result;
        }
        throw new ArgumentException($"Deserialized object is not of type {typeof(T)}");
    }

}
  • Throw an exception and try to serialize
ISerializer serializer = new MessagePackContractLessSerializer();
var exception = GetException();
var exceptionSerialized = serializer.Serialize(exception);

Exception GetException()
{
    try
    {
        throw new InvalidOperationException("Invalid Operation");
        //throw new InvalidOperationException("Invalid Operation", new ArgumentException("Inner Exception"));
    }
    catch (Exception ex)
    {
        return ex;
    }   
}

Expected behavior

Correct exception object should be received in Serialize method of the IMessagePackFormatter that was passed for serialization.

Actual behavior

null/inner exception object is received in Serialize method of the IMessagePackFormatter instead of actual exception passed for serialization.

Version used:

.Net Core 8.0 , MessagePack 2.5.172 : Broken
.Net Framework 4.7, MessagePack 2.5.172 : Partly broken (When no inner exception, we get the correct value. Same as .net core when there is inner exception)

To set expectations, the contractless side is very much a your-mileage-may-vary sort of thing. Exception serialization is particularly non-trivial and unlikely to produce a good result. When the .NET team come up with a recommendation for how to properly and securely support exception serialization, I bet messagepack will pick it up.

In the meantime, if you're not getting what you want from contractless, you should follow the README to author your own resolver/formatter for them.

I am trying to write a custom formatter however receiving null value for the exception object that I am trying to serialize. I am blocked with the custom formatter approach as a result.

  • Passing an exception object to serialize
    image
  • Getting null value in custom formatter for exception
    image

That's pretty weird. No idea why that would happen. I suggest you search the callstack to find which method is suppressing the value.

Or if you can't figure it out, paid support is an option.