https://github.com/elmahio/Exceptionator
Roslyn analyzers for improving exception handling in C# code.
https://github.com/elmahio/Exceptionator
best-practices exception roslyn roslyn-analyzer roslyn-analyzers
Last synced: 2 months ago
JSON representation
Roslyn analyzers for improving exception handling in C# code.
- Host: GitHub
- URL: https://github.com/elmahio/Exceptionator
- Owner: elmahio
- Created: 2025-06-25T08:57:58.000Z (12 months ago)
- Default Branch: main
- Last Pushed: 2025-11-20T17:49:23.000Z (7 months ago)
- Last Synced: 2026-02-02T01:38:50.357Z (5 months ago)
- Topics: best-practices, exception, roslyn, roslyn-analyzer, roslyn-analyzers
- Language: C#
- Homepage: https://marketplace.visualstudio.com/items?itemName=elmahio.theexceptionator
- Size: 6.44 MB
- Stars: 8
- Watchers: 1
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
Awesome Lists containing this project
- awesome-analyzers - Exceptionator - Roslyn analyzers for improving exception handling in C# code. (Popular Analyzers / Analyzer collections)
README
# The Exceptionator

Roslyn analyzers for improving exception handling in C# code.
This project includes a set of diagnostics aimed at encouraging better exception practices. Each rule detects common pitfalls and helps improve reliability, maintainability, and clarity of your exception logic.
## Rules
### EX001: Exception should include a message
Ensures that exceptions are instantiated with a meaningful message.
❌ Bad:
```csharp
throw new InvalidOperationException();
```
✅ Good:
```csharp
throw new InvalidOperationException("Operation failed due to ...");
```
### EX002: Avoid throwing base exceptions
Avoids throwing base exceptions like System.Exception or System.SystemException.
❌ Bad:
```csharp
throw new Exception("Something went wrong");
```
✅ Good:
```csharp
throw new InvalidOperationException("Something went wrong");
```
### EX003: Missing inner exception
Ensures that newly thrown exceptions inside catch blocks include the original exception as inner exception.
❌ Bad:
```csharp
catch (Exception ex)
{
throw new CustomException("Something failed");
}
```
✅ Good:
```csharp
catch (Exception ex)
{
throw new CustomException("Something failed", ex);
}
```
### EX004: Use 'throw;' instead of 'throw ex;'
Preserves the original stack trace when rethrowing exceptions.
❌ Bad:
```csharp
catch (Exception ex)
{
throw ex;
}
```
✅ Good:
```csharp
catch (Exception ex)
{
throw;
}
```
### EX005: Exception variable is unused
Detects catch variables that are never used.
❌ Bad:
```csharp
catch (Exception ex) { LogError(); }
```
✅ Good:
```csharp
catch (Exception ex) { LogError(ex); }
```
### EX006: Unreachable code after throw
Detects code written after a throw statement that will never execute.
❌ Bad:
```csharp
throw new Exception();
DoSomething();
```
✅ Good:
```csharp
DoSomething();
throw new Exception();
```
### EX007: Pointless try/catch block
Detects try/catch blocks that don't add meaningful handling logic.
❌ Bad:
```csharp
try
{
DoSomething();
}
catch (Exception)
{
throw;
}
```
✅ Good:
```csharp
DoSomething();
```
### EX008: ThreadAbortException must not be swallowed
Ensures ThreadAbortException is either rethrown or reset.
❌ Bad:
```csharp
catch (ThreadAbortException ex) { LogError(); }
```
✅ Good:
```csharp
catch (ThreadAbortException ex) { Thread.ResetAbort(); }
```
### EX009: Empty try block with catch
Detects try blocks that are empty while having a catch.
❌ Bad:
```csharp
try { } catch (Exception ex) { Log(ex); }
```
✅ Good:
```csharp
try { DoSomething(); } catch (Exception ex) { Log(ex); }
```
### EX010: Task.WaitAll should be wrapped with AggregateException catch
Ensures proper exception handling for Task.WaitAll by requiring AggregateException catch or using Task.WhenAll.
❌ Bad:
```csharp
try { Task.WaitAll(tasks); } catch (Exception ex) { Log(ex); }
```
✅ Good:
```csharp
try { Task.WaitAll(tasks); } catch (AggregateException ex) { Log(ex); }
```
### EX011: Empty catch block
Detects catch blocks that are completely empty without even a comment.
❌ Bad:
```csharp
try { DoSomething(); } catch (Exception) { }
```
✅ Good:
```csharp
try { DoSomething(); } catch (Exception) { /* intentionally ignored */ }
```
### EX012: Don't throw exceptions from property getters
Discourages throwing exceptions directly from property getters.
❌ Bad:
```csharp
public string Name => throw new Exception();
```
✅ Good:
```csharp
public string Name => _name ?? "";
```
### EX013: Avoid throwing ex.InnerException
Detects when ex.InnerException is thrown directly, which may cause null reference issues and loses the stack trace.
❌ Bad:
```csharp
catch (Exception ex) { throw ex.InnerException; }
```
✅ Good:
```csharp
catch (Exception ex) { throw; }
```
### EX014: Avoid logging only ex.Message
Suggests logging the full exception instead of just the message to retain full context.
❌ Bad:
```csharp
LogError(ex.Message);
```
✅ Good:
```csharp
LogError(ex);
```
### EX015: Avoid logging ex.ToString()
Recommends logging the exception directly rather than calling ToString.
❌ Bad:
```csharp
LogError("Error: " + ex.ToString());
```
✅ Good:
```csharp
LogError(ex);
```
### EX016: Avoid throwing null – use a specific exception type instead.
❌ Bad:
```csharp
throw null;
```
✅ Good:
```csharp
throw new InvalidOperationException("An error");
```
### EX017: Avoid when clauses that always evaluate to true
Detects `when` filters on catch blocks that always return true and are thus redundant.
❌ Bad:
```csharp
catch (Exception ex) when (true) { Handle(ex); }
```
✅ Good:
```csharp
catch (Exception ex) when (ex is IOException) { Handle(ex); }
```
### EX018: Filter exceptions manually inside catch
Detects catch blocks that catch a broad exception and then manually filter using if (`ex is ...`). Prefer catch filters (`when`) or catching specific exception types instead.
❌ Bad:
```csharp
try { ... }
catch (Exception ex)
{
if (ex is ArgumentException)
HandleArgument();
}
```
✅ Good:
```csharp
try { ... }
catch (ArgumentException)
{
HandleArgument();
}
```
### EX019: NotImplementedException left in code
Detects `throw new NotImplementedException()` left in methods or properties.
❌ Bad:
```csharp
public void DoWork() => throw new NotImplementedException();
```
✅ Good:
```csharp
public void DoWork() => ActualImplementation();
```
### EX020: Exception class should be public
Ensures that exception types are declared `public` to be visible when thrown or caught across assemblies.
❌ Bad:
```csharp
class CustomException : Exception
{
}
```
✅ Good:
```csharp
public class CustomException : Exception
{
}
```
### EX021: Missing expected constructors on custom exception
Ensures that custom exceptions implement the expected constructors with message and inner exception parameters.
❌ Bad:
```csharp
public class MyCustomException : Exception
{
}
```
✅ Good:
```csharp
public class MyCustomException : Exception
{
public MyCustomException(string message) : base(message) { }
public MyCustomException(string message, Exception innerException)
: base(message, innerException) { }
}
```
### EX022: Exception constructors must call base
Ensures that exception constructors pass their parameters (message, innerException) to the base constructor.
❌ Bad:
```csharp
public MyCustomException(string message) { }
public MyCustomException(string message, Exception inner) { }
```
✅ Good:
```csharp
public MyCustomException(string message) : base(message) { }
public MyCustomException(string message, Exception inner) : base(message, inner) { }
```
### EX023: Exception class name must end with 'Exception'
Ensures consistency and clarity by requiring exception classes to follow the naming convention of ending with 'Exception'.
❌ Bad:
```csharp
public class MyCustomError : Exception
{
}
```
✅ Good:
```csharp
public class MyCustomException : Exception
{
}
```
### EX024: Avoid catching fatal exceptions like StackOverflowException or ExecutionEngineException
Flags catch blocks that handle fatal exceptions which should not be caught or are uncatchable.
❌ Bad:
```csharp
try { ... }
catch (StackOverflowException ex) { Log(ex); }
```
✅ Good:
```csharp
try { ... }
catch (Exception ex) { Log(ex); }
```
### EX025: Handle documented exceptions or propagate them explicitly
Flags calls to methods that document thrown exceptions (via `` XML comments) when the caller does not handle or document those exceptions.
❌ Bad:
```csharp
public void Method1()
{
Method2(); // Method2 documents that it may throw NullReferenceException
}
///
///
///
public void Method2()
{
var x = 1 / 0;
}
```
✅ Good:
```csharp
public void Method1()
{
try
{
Method2();
}
catch (NullReferenceException ex)
{
throw;
}
}
///
///
///
public void Method2()
{
var x = 1 / 0;
}
```
### EX026: Document all explicitly thrown exceptions when using `` tags
Flags methods that already document one or more exceptions using `` XML tags but forget to document additional exceptions that are explicitly thrown within the method body.
❌ Bad:
```csharp
///
/// Does something.
///
///
public void M()
{
throw new InvalidOperationException();
throw new ArgumentException("arg"); // not documented
}
```
✅ Good:
```csharp
///
/// Does something.
///
///
///
public void M()
{
throw new InvalidOperationException();
throw new ArgumentException("arg");
}
```
## Acknowledgments
* [Davide Bellone](https://github.com/bellons91)
* [Andrew Lock](https://github.com/andrewlock)
* [Marc Jacobi](https://github.com/obiwanjacobi)
---
Sponsored by [elmah.io](https://elmah.io).