runtime: NullReferenceException - Completely devoid of details

I don’t know if this necessarily the right place for this, but the payoff is big enough to warrant a possible failed attempt.

NullReferenceExceptions are the most common exceptions your average developer is going to encounter, and yet the only information that is included in the exception is the message that something was null and you may or may not get a line number in the stacktrace.

Take the following example:

_orderService.CreateOrder(items.ToList(), customer.CustomerId);

If I get a NullReferenceException on this line, there are three different objects that could have been null and caused the error (_orderService, items and customer). Knowing that it was specifically customer that was null would be incredibly helpful. Extending this concept further, having a small dump of all locally scoped variables when the exception was thrown would be incredibly useful, but I’ll go with baby steps and just settle for knowing which variable/expression was the root cause of the null reference exception.

About this issue

  • Original URL
  • State: open
  • Created 9 years ago
  • Reactions: 43
  • Comments: 45 (21 by maintainers)

Commits related to this issue

Most upvoted comments

Is there a plan to also update the exception message? Surely that is really the important part of this?

Has there been any progress on this topic? I think it would be very helpful to introduce more detailed exceptions like Java does. For example code like this.

String s = null;
String s1 = s.toLowerCase();

Java will output a very helpful exception message.

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.toLowerCase()" because "s" is null
	at org.jdk17.App.main(App.java:14)

The exact method and reason for the exception are clear at a glance. If there are multiple methods and variables in a line of code, you can quickly locate the problem.

Related: https://openjdk.java.net/jeps/358 JEP 358: Helpful NullPointerExceptions

This feature is indeed available in VS 2017, article says it is not available for .NET Core/UWP, only available for .NET 4.6.2.

Would be really great to have that not in the VS but in .NET Core/Mono so that we can have that information in the crash reports rather than when debugging in VS.

I believe that NPE can be debugged in quite straightforward manner using crashdumps and WinDBG right now.

I don’t think that anything that involves crashdumps or WinDBG can be called straightforward. Especially if you consider that NRE is an exception that I think beginners often encounter.

NPEs are often caught

I hope that you are wrong about this. NRE pretty much always indicates a bug and catching it just hides that bug.

Is this available in .Net Core 3.0 ?

It looks like VS 2017 will show the information you want when targeting .NET Framework 4.6.2 or higher: https://blogs.msdn.microsoft.com/devops/2016/03/31/using-the-new-exception-helper-in-visual-studio-15-preview/

Null reference debug helper

I’m not sure whether it’s enabled in .NET Core

Is this available in .Net Core 3.0 ?

The debugger experience is:

image

But the exception message is unchanged.

@jkotas Is any type information available at the point the exception is generated?

This information is not available today. Adding it would be same problem as https://github.com/dotnet/runtime/issues/3858#issuecomment-72954338

@jkotas Is any type information available at the point the exception is generated?

Even if the variable name is unavailable, there are a number of scenarios where knowing the expected type of the null reference would be helpful (even the compile-time type) .

A (very) contrived example:

this.authorisedUsers
    .FirstOrDefault() 
    .Manager
    .ContactDetails
    .Address
    .Postcode
    .ToUpper();

If the NRE told you the type was string, then you’d know the problem was Postcode. If it told you that it was Person then it’s ambiguous, but it’s at least narrowed down the possibilities.

Obviously it wouldn’t help with something like

foo(a.ToString(), b.ToString(), c.ToString());

Correct, the JIT and PDB do not have enough precision in debug info today to reliably identify the source of NullReferenceException. For example, the following C# statement:

        foo(a.ToString(), b.ToString(), c.ToString());

has just one sequence point, and so it is not possible to identify from just the NullReferenceException location and debug info which one of a, b or c was null. If you would like to see the good error message even with JIT optimizations on, it is even harder because of the debug info tracking is not guaranteed to be preserved by the JIT optimizations today.

I think that the first step to attack this problem would be to write a design proposal about possible approaches. The ones identified by @mikedn would be a good start. Focus on:

  • The rough sketch of extra information that would flow between the different components in the system
  • The user experience - how good and reliable would be the exception messages
  • The estimated overhead in disk footprint, RAM and CPU cycles

I know it is a lot of work…

@vongillern I think you can easily make that even better by including the name of the invoked method. Something like (mostly copying the existing message):

NullReferenceException: Object reference not set to an instance of an object when calling ‘SuperOrderService.CreateOrder’.

This shouldn’t be that hard to do, since the callvirt instruction that causes the vast majority of NREs knows which method it was.

Agreed. I’m not sure it’d be “easy”, but well worth the effort. If the name was not available, including its type in the error message would make it unambiguous 95% of the time. (i.e. NullReferenceException - expression of type ‘SuperOrderService’ evaluated to null).

My guess though, is that if the PDB is available, it would know the character ranges of the null expression. If i turn on “break on thrown exceptions” in the exceptions dialog box, it always breaks me in at the right point and I can inspect all of my variables. I just want a dump so I don’t have to be actively debugging.