System.Exception
System.Exception is the base class of every exception in .NET. When you see a plain Exception in a log, someone wrote throw new Exception("..."), or a library did, and the message is all you have to go on.
The type itself says nothing about what went wrong, so the message, the stack trace and the inner exceptions are what matter. This page covers why a plain Exception is a poor choice to throw, and how to catch, wrap and rethrow exceptions without losing information.
Minimum version: >= 1.1 >= Core 1.0
Statistics
Common causes
A plain Exception is always thrown by code, on purpose. These are the typical sources.
Your own code throws it
Throwing the base type is quick, but it forces callers to catch everything to handle it, and hides which kind of failure it was.
public void Validate(int quantity)
{
if (quantity <= 0)
{
throw new Exception("Quantity must be positive");
}
}
Throw the type that describes the failure. For an invalid argument, that is ArgumentOutOfRangeException:
public void Validate(int quantity)
{
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(quantity);
}
A library wraps an error in a plain Exception
Some libraries throw Exception for everything, particularly older ones, and ones that report errors from a native component. You cannot change it, but you can wrap it in your own type close to the call.
Catch it at the call to the library, and throw an exception of your own with the original as the inner exception:
public class PaymentFailedException(string message, Exception inner) : Exception(message, inner);
public void Charge(Action gateway)
{
try
{
gateway();
}
catch (Exception e) when (e is not OperationCanceledException)
{
throw new PaymentFailedException("The payment gateway failed", e);
}
}
An exception that was rethrown in the wrong way
throw e; in a catch block resets the stack trace to the line of the throw. The original location is gone, and the trace shows the rethrow instead.
try
{
int.Parse("abc");
}
catch (Exception e)
{
throw e; // the stack trace now starts here
}
Use throw; without the variable. It keeps the original stack trace:
try
{
int.Parse("abc");
}
catch (Exception e)
{
logger.LogError(e, "Parsing failed");
throw;
}
How to fix it and prevent it
Throw the most specific type
Use the built-in types where they fit, such as ArgumentException, InvalidOperationException and NotSupportedException. Derive your own type from Exception when none of them describes the failure.
public class OutOfStockException(string sku) : Exception($"Product {sku} is out of stock")
{
public string Sku { get; } = sku;
}
Preserve the stack trace and the cause
Use throw; to rethrow, and pass the original exception as the inner exception when you wrap it. A log that only has the outer exception loses the line where the failure started.
Catch Exception only at the edges
A catch of Exception belongs at the boundary of the application, such as the middleware that logs unhandled errors, a background job loop, or a message handler. In other places, catch the types you know how to handle.
try
{
int.Parse("abc");
}
catch (FormatException)
{
// handle the case we know
}
catch (Exception e) when (e is not OperationCanceledException)
{
logger.LogError(e, "Unexpected error");
throw;
}
How to read the stack trace
There is not much in the exception itself, so look at the message and where it was thrown:
System.Exception: Quantity must be positive
at Shop.Services.OrderValidator.Validate(Int32 quantity) in C:\src\Shop\Services\OrderValidator.cs:line 12
at Program.<Main>$(String[] args) in C:\src\Shop\Program.cs:line 4
The message says Quantity must be positive, and the top frame shows that line 12 of OrderValidator.cs threw it. If the trace is shorter than you expect, or the first frame is a catch block, the exception may have been rethrown with throw e;.
Should you catch it?
Catching Exception also catches the exceptions you cannot do anything about, such as OutOfMemoryException. Use it to log and rethrow, or at the outermost level, where the alternative is a crash.
Avoid empty catch blocks. An exception that is swallowed leaves no trace, and the bug shows up somewhere else later.
Find Exception before your users do
elmah.io logs every unhandled exception in your .NET application with its stack trace and request details, groups identical errors and notifies you when a new one appears.
Start free trialRelated exceptions
- ApplicationException is an old base class for custom exceptions, which should not be used.
- AggregateException wraps several exceptions, for example from tasks.
- ArgumentException is the type to throw for arguments that are not valid.
Frequently asked questions
Should I throw System.Exception?
No. Throw a specific type that tells the caller what happened. Code analysis rule CA2201 reports it. A plain Exception forces callers to catch everything.
What is the difference between throw and throw e?
throw; rethrows the current exception and keeps its stack trace. throw e; throws it again as if it started there, and replaces the stack trace.
Is it bad to catch Exception?
It is fine at the edges of the application, to log and continue or to give the user an error message. Inside the code, catch the types you can handle.
How do I get the real cause of an exception?
Follow InnerException to the end, or call GetBaseException(). Logging the exception with a logger or the elmah.io client includes the inner exceptions.
Let your AI agent track it down
Connect Claude Code, Cursor, VS Code or Visual Studio to the elmah.io MCP server and ask your agent to look into Exception for you. For example:
The agent reads the stack trace and request details from elmah.io, finds the code in your repository and proposes a fix. In Claude Code, add the server with one command:
claude mcp add --transport http --client-id claudecode elmahio https://mcp.elmah.io/mcp
The MCP server is included on every plan and is currently in beta. Set up the MCP server.
Further reading
YouTube videos
Answers from Stack Overflow
Catch System.Exception and switch on the types
catch (Exception ex)
{
if (ex is FormatException || ex is OverflowException)
{
WebId = Guid.Empty;
}
else
throw;
}
By Joseph Daigle. Read the original answer on Stack Overflow.
EDIT: I do concur with others who are saying that, as of C# 6.0, exception filters are now a perfectly fine way to go: catch (Exception ex) when (ex is ... || ex is ... )
Except that I still kind of hate the one-long-line layout and would personally lay the code out like the following. I think this is as functional as it is aesthetic, since I believe it improves comprehension. Some may disagree:
catch (Exception ex) when (
ex is ...
|| ex is ...
|| ex is ...
)
ORIGINAL:
I know I'm a little late to the party here, but holy smoke...
Cutting straight to the chase, this kind of duplicates an earlier answer, but if you really want to perform a common action for several exception types and keep the whole thing neat and tidy within the scope of the one method, why not just use a lambda/closure/inline function to do something like the following? I mean, chances are pretty good that you'll end up realizing that you just want to make that closure a separate method that you can utilize all over the place. But then it will be super easy to do that without actually changing the rest of the code structurally. Right?
private void TestMethod ()
{
Action<Exception> errorHandler = ( ex ) => {
// write to a log, whatever...
};
try
{
// try some stuff
}
catch ( FormatException ex ) { errorHandler ( ex ); }
catch ( OverflowException ex ) { errorHandler ( ex ); }
catch ( ArgumentNullException ex ) { errorHandler ( ex ); }
}
I can't help but wonder (warning: a little irony/sarcasm ahead) why on earth go to all this effort to basically just replace the following:
try
{
// try some stuff
}
catch( FormatException ex ){}
catch( OverflowException ex ){}
catch( ArgumentNullException ex ){}
...with some crazy variation of this next code smell, I mean example, only to pretend that you're saving a few keystrokes.
// sorta sucks, let's be honest...
try
{
// try some stuff
}
catch( Exception ex )
{
if (ex is FormatException ||
ex is OverflowException ||
ex is ArgumentNullException)
{
// write to a log, whatever...
return;
}
throw;
}
Because it certainly isn't automatically more readable.
Granted, I left the three identical instances of /* write to a log, whatever... */ return; out of the first example.
But that's sort of my point. Y'all have heard of functions/methods, right? Seriously. Write a common ErrorHandler function and, like, call it from each catch block.
If you ask me, the second example (with the if and is keywords) is both significantly less readable, and simultaneously significantly more error-prone during the maintenance phase of your project.
The maintenance phase, for anyone who might be relatively new to programming, is going to compose 98.7% or more of the overall lifetime of your project, and the poor schmuck doing the maintenance is almost certainly going to be someone other than you. And there is a very good chance they will spend 50% of their time on the job cursing your name.
And of course FxCop barks at you and so you have to also add an attribute to your code that has precisely zip to do with the running program, and is only there to tell FxCop to ignore an issue that in 99.9% of cases it is totally correct in flagging. And, sorry, I might be mistaken, but doesn't that "ignore" attribute end up actually compiled into your app?
Would putting the entire if test on one line make it more readable? I don't think so. I mean, I did have another programmer vehemently argue once long ago that putting more code on one line would make it "run faster." But of course he was stark raving nuts. Trying to explain to him (with a straight face--which was challenging) how the interpreter or compiler would break that long line apart into discrete one-instruction-per-line statements--essentially identical to the result if he had gone ahead and just made the code readable instead of trying to out-clever the compiler--had no effect on him whatsoever. But I digress.
How much less readable does this get when you add three more exception types, a month or two from now? (Answer: it gets a lot less readable).
One of the major points, really, is that most of the point of formatting the textual source code that we're all looking at every day is to make it really, really obvious to other human beings what is actually happening when the code runs. Because the compiler turns the source code into something totally different and couldn't care less about your code formatting style. So all-on-one-line totally sucks, too.
Just saying...
// super sucks...
catch( Exception ex )
{
if ( ex is FormatException || ex is OverflowException || ex is ArgumentNullException )
{
// write to a log, whatever...
return;
}
throw;
}
By Craig Tullis. Read the original answer on Stack Overflow.
Exception filters are now available in c# 6+. You can do
try
{
WebId = new Guid(queryString["web"]);
}
catch (Exception ex) when(ex is FormatException || ex is OverflowException)
{
WebId = Guid.Empty;
}
In C# 7.0+, you can combine this with pattern matching too
try
{
await Task.WaitAll(tasks);
}
catch (Exception ex) when( ex is AggregateException ae &&
ae.InnerExceptions.Count > tasks.Count/2)
{
//More than half of the tasks failed maybe..?
}
By Mat J. Read the original answer on Stack Overflow.
If you can upgrade your application to C# 6 you are lucky. The new C# version has implemented Exception filters. So you can write this:
catch (Exception ex) when (ex is FormatException || ex is OverflowException) {
WebId = Guid.Empty;
}
Some people think this code is the same as
catch (Exception ex) {
if (ex is FormatException || ex is OverflowException) {
WebId = Guid.Empty;
}
else {
throw;
}
}
But it´s not. Actually this is the only new feature in C# 6 that is not possible to emulate in prior versions. First, a re-throw means more overhead than skipping the catch. Second, it is not semantically equivalent. The new feature preserves the stack intact when you are debugging your code. Without this feature the crash dump is less useful or even useless.
See a discussion about this on CodePlex. And an example showing the difference.
By AArteDoCodigo.com.br - Maniero. Read the original answer on Stack Overflow.
Update for C# 9
Using the new pattern matching enhancements made in C# 9, you can shorten the expression in the exception filter. Now, catching multiple exceptions is a simple is this:
try
{
WebId = new Guid(queryString["web"]);
}
catch (Exception e) when (e is FormatException or OverflowException)
{
WebId = Guid.Empty;
}
By Nechemia Hoffmann. Read the original answer on Stack Overflow.
Source: Stack Overflow