I recently found myself in one of those troubleshooting scenarios where you just can't use a proper debugger. Along the way, I discovered a bug that I would have solved in a couple of minutes with a debugger, but without one, it had me stumped for several days. It was an interesting little bug that was very frustrating to track down. I hope that by sharing this information you won't get stuck on it as long as I did if you encounter it.
Here's a sample piece of code:
var x = "t";
var y = int.Parse(x);
catch (Exception ex)
Console.WriteLine("Exception: " + ex.Message);
Console.WriteLine("Exception Inner Message: " + ex.InnerException.Message);}
You would expect the following to happen:
- Variable x is created as a string, with a value of "t".
- Enter a try block.
- int.Parse throws an exception, because "t" cannot be parsed as an integer.
- The exception's message property is printed to the screen.
- The inner exception's message property is printed to the screen.
The reality is step #5 never happens; instead, the application completely bombs out, with this exception message: "Object reference not set to an instance of an object." While the problem is easily caught in an IDE debugging session, in a scenario where you can't get a debugger attached (and we know those seem to happen all too often for a variety of reasons), we are pretty much out of luck. What's going on?
We are seeing a null reference occurring within the catch block itself, because there is no guarantee that ex.InnerMessage is not null. And because of where the exception is happening, a lot of our usual methods of tracking the issue down are not available to us (event viewer, for example, seems to log a lot less information than you'd normally get). Even in a debugger, we are at a disadvantage; there is no Exception object on the stack for us to find — as soon as we clear the Exception notice on the screen, there are no details of this exception anywhere. If you inspect the Exception object in the catch block, it shows the integer parsing issue, not this null reference item.
I learned two lessons here. First, you need to be very careful within catch blocks; exceptions within them will be hard to track down, especially if you cannot get a debugger attached. Second, you need to be aware that Exception.InnerMessage is an object reference and needs to be treated as such. I know this is pretty obvious, but if you are used to only using a couple of Exception's properties in code like I am (I inspect InnerMessage all of the time in the debugger, but I never used it in the code itself before), this can bite you pretty hard.
Justin James is the Lead Architect for Conigent.