Exceptions explained
Back to articles.The previous article explained the purpose of exceptions and their benefits. This article aims at describing exception classes and how you use them.
A gentle introduction
We will go through the exception class in detail soon, but let's start with a small usage example. Everything will be explained in detail later in the article.
try
{
throw new InvalidOperationException("Failed to load resource 'Hello world!'.");
}
catch (InvalidOperationException exception)
{
Console.WriteLine(exception.Message);
}
A new exception object of the type InvalidOperationException is
created in the above example. It's constructed with an error message
stating that the resource could not be loaded. The purpose of the error
message is to explain why we decided to throw an exception.
Next, we use the throw keyword with the created exception object.
throw tells .NET to stop running the code and send the exception up
the call stack until it finds a catch block in any of the invoked
methods. That is also called exception propagation.
Finally, we have a try/catch block. The purpose of the catch block
is to tell .NET that we want to handle a specific type of exception. The
exception can have been thrown inside the try block or any called
method within the try block.
The output from the application will be the following.

Conclusion
- The message in the exception constructor should be used to explain why an exception was thrown.
throwis used to abort the current execution flow when a method cannot deliver what it promised.tryis used to tell which code any potential exceptions should be handled for.catchis used to handle exceptions, including all exceptions that inherit the specified one.
Exception classes
Exception classes are used to represent exceptions. The base class,
Exception, defines some properties that all exceptions will share.
Exception class names should always end with Exception per .NETs
naming conventions, like InvalidDataException or
SecurityException.
There are two properties on the Exception class that are more
important than the other ones. Those two are specified below, to learn
about the rest of the properties, read the
documentation
in MSDN.
Message
The first property is the Message property since it typically explains
why the exception was thrown.
The property value is in most cases specified in the constructor of an exception:
new KeyNotFoundException("Failed to find the given key `meta` in the dictionary.");
new EntityNotFoundException("User 'Lars' was not found.");
new ArgumentException("UserId must be alphanumeric.");
StackTrace
The stack trace is like breadcrumbs. It tells where in the code that the exception originated from. The first line in the stack trace is the method that the exception was thrown in. The next line is the method that called the failing method and so on.
Let's take the earlier code example, but with complete source and with the stack trace printed instead.
using System;
namespace Exception1
{
class Program
{
static void Main(string[] args)
{
try
{
throw new InvalidOperationException("Failed to load resource 'Hello world!'.");
}
catch (Exception exception)
{
Console.WriteLine(exception.StackTrace);
}
}
}
}
The code will generate the following output:
at Exception1.Program.Main(String[] args) in C:\src\junk\Exceptions\Exception1\Exception1\Program.cs:line 11
The output shows that the exception was thrown in the Main method in
the class Program.cs. The line number is included since this
application had the
pdb
file distributed with it.
Conclusion
- Exceptions are classes that inherit the
Exceptionbase class. - The
StackTraceproperty shows where in the application that the exception was thrown and how that method was called. - The
Messageproperty specifies why an exception was thrown. - To get line numbers in the stack trace, include all
pdbfiles from the build output.
Exception propagation
The stack trace is essential when wanting to understand where an exception was thrown. It shows which path the code took in the application before the exception was thrown. Having that knowledge makes it easier to reproduce the cause of the generated the exception.
The stack trace is never prepopulated but is instead appended each time the .NET exception handling mechanism travels up the call stack. That process is called exception propagation.
Consider the following example:
var myException = new InvalidOperationException("Failed to load resource 'Hello world!'.");
Console.WriteLine(myException.StackTrace);
The output from exception.StackTrace will be null because it's the
throw statement that starts to fill the stack trace.
Wrapping the example in a try/catch will populate the stack trace:
using System;
namespace Exception1
{
class Program
{
static void Main(string[] args)
{
try
{
var myException = new InvalidOperationException("Failed to load resource 'Hello world!'.");
throw myException;
}
catch (Exception exception)
{
Console.WriteLine(exception.StackTrace);
}
}
}
}
Output
at Exception1.Program.Main(String[] args) in C:\src\junk\Exceptions\Exception1\Exception1\Program.cs:line 12
Likewise, calling a method from the try block will produce two lines in the stack trace.
using System;
namespace Exception1
{
class Program
{
static void Main(string[] args)
{
try
{
AnotherMethod();
}
catch (Exception exception)
{
Console.WriteLine(exception.StackTrace);
}
}
public static void AnotherMethod()
{
throw new InvalidOperationException("Failed to load resource 'Hello world!'.");
}
}
}
Output
at Exception1.Program.AnotherMethod() in C:\src\junk\Exceptions\Exception1\Exception1\Program.cs:line 21
at Exception1.Program.Main(String[] args) in C:\src\junk\Exceptions\Exception1\Exception1\Program.cs:line 11
Observe that the Main() method is last in the stack trace. It's
because it was appended as the stack unwinds from the deepest call to
the first one.
Having three calls works the same.
using System;
namespace Exception1
{
class Program
{
static void Main(string[] args)
{
try
{
AnotherMethod();
}
catch (Exception exception)
{
Console.WriteLine(exception.StackTrace);
}
}
public static void AnotherMethod()
{
DeepestMethod();
}
public static void DeepestMethod()
{
throw new InvalidOperationException("Failed to load resource 'Hello world!'.");
}
}
}
Output
at Exception1.Program.DeepestMethod() in C:\src\junk\Exceptions\Exception1\Exception1\Program.cs:line 26
at Exception1.Program.AnotherMethod() in C:\src\junk\Exceptions\Exception1\Exception1\Program.cs:line 21
at Exception1.Program.Main(String[] args) in C:\src\junk\Exceptions\Exception1\Exception1\Program.cs:line 11
DeepestMethod did not have a try block, nor did AnotherMethod but
Main did. Thus the exception traveled three steps before getting to a
catch block.
Having a catch block inside AnotherMethod would stop the exception
propagation:
using System;
namespace Exception1
{
class Program
{
static void Main(string[] args)
{
try
{
AnotherMethod();
}
catch (Exception exception)
{
Console.WriteLine(exception.StackTrace);
}
}
public static void AnotherMethod()
{
try
{
DeepestMethod();
}
catch (Exception exception)
{
Console.WriteLine("Handled in AnotherMethod: " + exception.Message);
}
}
public static void DeepestMethod()
{
throw new InvalidOperationException("Failed to load resource 'Hello world!'.");
}
}
}
Output

Notice that the console writes from Main is not printed, since
exception propagation stops at the first found catch block.
Conclusions
- Exception propagation is when the exception travels up the method calls (i.e. stack frames).
- The stack trace is appended for each method call that the exception travels to.
- Exception propagation stops when a
catchblock is found.
Rethrowing exceptions
Sometimes you want to catch exceptions to be able to process them in some way but still let the upper stack frames be able to handle the same exception.
That can be achieved by having another throw in the catch block.
using System;
namespace Exception1
{
class Program
{
static void Main(string[] args)
{
try
{
AnotherMethod();
}
catch (Exception exception)
{
Console.WriteLine(exception.StackTrace);
}
}
public static void AnotherMethod()
{
try
{
throw new InvalidOperationException("Sample exception");
}
catch (Exception ex)
{
Console.WriteLine("Exception was here: " + ex);
Console.WriteLine();
throw;
}
}
}
}
Output
Exception was here: Sample exception
at Exception1.Program.AnotherMethod() in C:\src\junk\Exceptions\Exception1\Exception1\Program.cs:line 28
at Exception1.Program.Main(String[] args) in C:\src\junk\Exceptions\Exception1\Exception1\Program.cs:line 11
The first two lines are from the Console.WriteLine calls in
AnotherMethod and the stack trace is from Console.WriteLine in
Main().
Using throw in catch blocks tells .NET that it should continue with
the exception propagation.
Avoid throw ex; when rethrowing exceptions
Do not use throw exception; in catch blocks:
try
{
throw new InvalidOperationException("Sample exception");
}
catch (Exception ex)
{
throw ex;
}
Using throw ex; tells .NET that it should start a new exception
propagation using your previously created exception object. As in such,
the stack trace will be cleared and built from scratch again.
A complete example:
using System;
namespace Exception1
{
class Program
{
static void Main(string[] args)
{
try
{
AnotherMethod();
}
catch (Exception exception)
{
Console.WriteLine(exception.StackTrace);
}
}
public static void AnotherMethod()
{
try
{
DeepestMethod();
}
catch (Exception ex)
{
Console.WriteLine("Exception was here: " + ex.Message);
Console.WriteLine();
throw ex;
}
}
public static void DeepestMethod()
{
throw new InvalidOperationException("Failed to load resource 'Hello world!'.");
}
}
}
Output
Exception was here: Failed to load resource 'Hello world!'.
at Exception1.Program.AnotherMethod() in C:\src\junk\Exceptions\Exception1\Exception1\Program.cs:line 28
at Exception1.Program.Main(String[] args) in C:\src\junk\Exceptions\Exception1\Exception1\Program.cs:line 11
See? The DeepestMethod is nowhere to be found in the stack trace,
making it impossible to find where the exception originated from.
Conclusion
- Rethrowing can be done to process the exception before letting it continue to travel up the call stack.
- Avoid
throw exas it tells .NET to treat the exception object as a new exception.