---
title: "Throwing exceptions - Why is my stack trace lost?"
description: "You might have read, that re-throwing an exception like this: `throw exc;` is considered bad practice and you should just do this: `throw;` instead.\n\nBut why is it like that? "
updated: 2022-10-21
tags: ["C#", ".NET", "Exception"]
canonical: "https://steven-giesel.com/blogPost/bc82fda9-eb47-47bd-850b-9d115a59a571/throwing-exceptions-why-is-my-stack-trace-lost"
---

# Throwing exceptions - Why is my stack trace lost?

You might have read, that re-throwing an exception like this: `throw exc;` is considered bad practice and you should just do this: `throw;` instead.

But why is it like that? For that, we have to discuss some core principles of exception handling. Let's see a small example here:
```csharp
try {
    Throws();
}
catch (Exception exc)
{
    Console.WriteLine("ohoh an exception");
    throw;
}

void Throws() => throw new Exception("??");
```

This will output something along the like this:
```no-class
ohoh an exception
Exception
System.Exception: ??
   at Program.<<Main>$>g__Throws|0_0()
   at Program.<Main>$(String[] args)
```

Now let's do the same, but we are "re-throwing" the exception object itself:
```csharp
try {
    Throws();
}
catch (Exception exc)
{
    Console.WriteLine("ohoh an exception");
    throw exc;
}

void Throws() => throw new Exception("??");
```

The output:
```no-class
ohoh an exception
Exception
System.Exception: ??
   at Program.<Main>$(String[] args)
```

Wait a second? There is something missing! Our `Throw` method is not in the stack-trace anymore! And that is exactly the difference when you re-throw like that. You will cut off the stack-trace until the catch block where you re-throw the object. Now there are **valid cases** where you really want this, mainly because of security concerns. I will not discuss this here in more detail.

**Why does that happen?**. To answer this you have to understand one important thing: **Exceptions are not immutable!** Let's have a look at the IL-code for this simplified version (and ignore the fact that the code itself is pretty dumb):
```csharp
try {
    throw new Exception("??");
}
catch (Exception exc)
{
    throw exc;
}

catch
{
    throw;
}
```
will be translated to:
```csharp
.try
{
    IL_0000: nop
    IL_0001: ldstr "\ud83d\udca3" // This is ?? in Unicode
    IL_0006: newobj instance void [System.Runtime]System.Exception::.ctor(string)
    IL_000b: throw // This is the throw new Exception("") part
} // end .try
catch [System.Runtime]System.Exception
{
    IL_000c: stloc.0
    IL_000d: nop
    IL_000e: ldloc.0
    IL_000f: throw // This is the throw exc part
} // end handler
catch [System.Runtime]System.Object
{
    IL_0010: pop
    IL_0011: nop
    IL_0012: rethrow // This is the throw; part
} // end handler
```

Two things are important here: The `throw new Exception` and `throw exc;` do have the same IL code. Only `throw;` has a different IL-Code: [`rethrow`](https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.rethrow?view=net-6.0). Here is where this mutability part comes into play. You as a developer, you don't have to set the stack-trace at all. So someone else has to do it and you can guess who is doing that: the [`throw`](https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.throw?view=net-6.0) keyword does that for you. So yes that is all the magic.

## Rethrowing with the original stack trace
I told you earlier that there are valid use cases to handle an exception and rethrow it later. If you still want to have the whole stack trace there is a helper class for you: [`ExceptionDispatchInfo`](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.exceptionservices.exceptiondispatchinfo?view=net-6.0). It offers a `Throw` method, which re-throws the saved exception with the given stack-trace.

```csharp
using System.Runtime.ExceptionServices;

ExceptionDispatchInfo? exceptionDispatchInfo;

try
{
    Throws();
}
catch (Exception exc)
{
    exceptionDispatchInfo = ExceptionDispatchInfo.Capture(exc);
}

// Do something here ...

if (exceptionDispatchInfo is not null)
    exceptionDispatchInfo.Throw();

void Throws() => throw new Exception("??");
```

Produces the following output:
```no-class
Exception
System.Exception: ??
   at Program.<<Main>$>g__Throws|0_0()
   at Program.<Main>$(String[] args)
--- End of stack trace from previous location ---
   at Program.<Main>$(String[] args)
```
