1

UPDATE: To avoid the problem, I tried explicitly checking for null, thinking that surely there's no way it could possible still throw the exception. It does. The following line throws a System.NullReferenceException:

if (ModelState == null)

How?!


We received an automated error report, showing that a particular line is throwing a NullReferenceException... but I cannot figure out how that particular line could possibly do that.

The line in question is if (ModelState?.IsValid == false). But... I've got a null conditional operator on there. How could it throw a NullReferenceException?

We haven't been able to figure out how to reproduce the error intentionally, but it keeps happening every now and then.

Here is the code (slightly pared down, but the relevant method left untouched:)

using AutoMapper;
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;

namespace MyApp.Web.Controllers
{
    public class ServiceCallController : Controller
    {
        [NotNull] private readonly IMapper _mapper;
        [NotNull] private readonly IServiceCallService _service;

        public ServiceCallController([NotNull] IMapper mapper, [NotNull] IServiceCallService service)
        {
            _mapper = mapper;
            _service = service;
        }

        [HttpPost]
        public ActionResult Index(ServiceCallViewModel vm)
        {
            if (ModelState?.IsValid == false)//This line throws "System.NullReferenceException: Object reference not set to an instance of an object."
            {
                Response.StatusCode = (int)HttpStatusCode.BadRequest;
                Response.TrySkipIisCustomErrors = true;
                return Json(new { message = string.Join("<br/>", ModelState.GetErrorList()) });
            }

            try
            {
                vm.Attachments.RemoveAll(a => a == null);
                var serviceCall = _mapper.Map<ServiceCallDto>(vm);
                _service.SubmitServiceCall(serviceCall);
                return Json(new { url = Url.Action("CallSearch", "Search") });
            }
            catch (Exception ex)
            {
                Response.StatusCode = (int)HttpStatusCode.BadRequest;
                Response.TrySkipIisCustomErrors = true;
                return Json(new { message = ex.Message });
            }
        }
    }

    public class ServiceCallDto
    {
        // ...bunch of properties...
    }

    public class ServiceCallViewModel
    {
        public List<HttpPostedFileBase> Attachments { get; set; }
        // ...bunch of properties...
    }

    public interface IServiceCallService
    {
        void SubmitServiceCall(ServiceCallDto serviceCall);
    }

    public static class ModelStateExtensions
    {
        public static List<string> GetErrorList([NotNull] this ModelStateDictionary modelState) =>
            (from item in modelState.Values
             from error in item.Errors
             select error.ErrorMessage).ToList();
    }
}

And here is the automated error email we received:

User Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 16_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.3 Mobile/15E148 Safari/604.1
System.NullReferenceException: Object reference not set to an instance of an object.
   at MyApp.Web.Controllers.ServiceCallController.Index(ServiceCallViewModel vm) in C:\Atlassian\bamboo-home\local-working-dir\CMF-MyApp-JOB1\Web\Controllers\ServiceCallController.cs:line 26
   at lambda_method(Closure , ControllerBase , Object[] )
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c.<BeginInvokeSynchronousActionMethod>b__9_0(IAsyncResult asyncResult, ActionInvocation innerInvokeState)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`2.CallEndDelegate(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass11_0.<InvokeActionMethodFilterAsynchronouslyRecursive>b__0()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass11_2.<InvokeActionMethodFilterAsynchronouslyRecursive>b__2()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass3_6.<BeginInvokeAction>b__4()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass3_1.<BeginInvokeAction>b__1(IAsyncResult asyncResult)

To further illustrate the issue:

My understanding is that ModelState?.IsValid evaluates to a bool? aka a Nullable<bool>. Which c#, as demonstrated in the snippet below, is able to compare against false even when null. So... how could that line possibly throw a NullReferenceException?

ModelStateDictionary nullState = null;
if (nullState?.IsValid == false)//Unlike what's happening in the real code in the wild, this line does NOT throw an error.
    System.Diagnostics.Debugger.Break();
else if (nullState?.IsValid == true)
    System.Diagnostics.Debugger.Break();
else if (nullState?.IsValid == null)
    System.Diagnostics.Debugger.Break();//The debugger stops on this line.
else
    System.Diagnostics.Debugger.Break();
5
  • 1
    How are you getting ModelState? Is it maybe a property whose getter can raise a NullReferenceException? Commented Aug 3, 2023 at 21:05
  • ServiceCallController inherits from System.Web.Mvc.Controller, which has the property public ModelStateDictionary ModelState { get; }. I can't see the code of the property getter since it's written by Microsoft. Commented Aug 3, 2023 at 21:12
  • You can actually see the source code, but it shouldn't happen in any case looking at it (also probably you would have a different stack trace). Are you totally sure that the code you posted is the one that is being executed? Could it be that the code that is running is an older version that has no null conditional operator? Commented Aug 3, 2023 at 21:30
  • I specifically changed the code to add the if (ModelState == null) check because the previous code was throwing the NullReferenceException on ModelState?.IsValid and when I did the stacktrace's line number changed accordingly, so yes, I am sure. Commented Aug 3, 2023 at 21:43
  • Looks like the nullability of ControllerContext was only added 2 years ago? This code predates that. Maybe that's the problem... I'll look into updating tomorrow, I guess. Commented Aug 3, 2023 at 22:01

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.