Truncated JSON response from web API

November 5th 2021 ASP.NET Core Serialization

An endpoint in my ASP.NET Core web API project suddenly started returning a truncated JSON response with a 200 response code. According to the logs, an exception was thrown, but for some reason the response code was not 500 as I would expect.

After some research, I found a GitHub issue that attributes this behavior to an exception being thrown during JSON serialization. According to the logs, the same thing happened in my case. The exception was thrown by the IEnumerable<T> when the elements were enumerated.

I managed to reproduce the behavior in a small sample project. The action method must return an IEnumerable<T>:

[HttpGet]
public IEnumerable<string> Get()
{
    return this.GetStrings();
}

The key to reproducing the behavior is in the implementation of this IEnumerable<T>. It must throw the exception after it has already returned several elements that have been successfully serialized. For example:

private IEnumerable<string> GetStrings()
{
    foreach (var i in Enumerable.Range(1, 1500))
    {
        yield return $"Item {i}";
    }

    throw new Exception();
}

The number of elements that must be returned before the exception is thrown depends on the size of the serialization output. Once the serialization output reaches a certain size, some the response starts being sent to the client. At this point, the response code is already set to 200 and part of the response has already been sent. If an exception is thrown after that, sending the rest of the response is interrupted, but the response code is not changed. Hence the truncated JSON.

In the example above, you can prevent this by iterating over the IEnumerable<T> before returning it from the action method:

[HttpGet]
public IEnumerable<string> Get()
{
    return this.GetStrings().ToList();
}

This will cause the exception to be thrown before the serialization starts, so a correct error response will be generated with a 500 response code.

However, this is not a perfect solution as it has a negative impact on performance. Moreover, it does not solve the problem in all cases. An exception during serialization could also be thrown at any other point during serialization, for example, when serializing a property of a complex object instead of enumerating the top-level objects of the response.

The real solution, therefore, requires that you ensure that serialization does not fail for the returned object. Exactly how this can be achieved depends on the individual case.

An example of a web API project that demonstrates the described behavior can be found in my GitHub repository.

An ASP.NET Core web API endpoint could return a truncated JSON body with a 200 response code when an exception is thrown during serialization of the object returned by the action method if a sufficient portion of the response has already been successfully serialized beforehand, so that part of the response has already been sent to the client. Fortunately, the exception will still be logged properly. The only way to reliably prevent this is to ensure that the returned object can be successfully serialized.

Get notified when a new blog post is published (usually every Friday):

Copyright
Creative Commons License