Skip to content
| Marketplace
Sign in
Visual Studio>Tools>The Exceptionator
The Exceptionator

The Exceptionator

elmahio

|
17 installs
| (0) | Free
Roslyn analyzers to catch common exception anti-patterns in .NET code.
Download

The Exceptionator

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:

throw new InvalidOperationException();

✅ Good:

throw new InvalidOperationException("Operation failed due to ...");

EX002: Avoid throwing base exceptions

Avoids throwing base exceptions like System.Exception or System.SystemException.

❌ Bad:

throw new Exception("Something went wrong");

✅ Good:

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:

catch (Exception ex)
{
    throw new CustomException("Something failed");
}

✅ Good:

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:

catch (Exception ex)
{
    throw ex;
}

✅ Good:

catch (Exception ex)
{
    throw;
}

EX005: Exception variable is unused

Detects catch variables that are never used.

❌ Bad:

catch (Exception ex) { LogError(); }

✅ Good:

catch (Exception ex) { LogError(ex); }

EX006: Unreachable code after throw

Detects code written after a throw statement that will never execute.

❌ Bad:

throw new Exception();
DoSomething();

✅ Good:

DoSomething();
throw new Exception();

EX007: Pointless try/catch block

Detects try/catch blocks that don’t add meaningful handling logic.

❌ Bad:

try { DoSomething(); } catch (Exception) { throw; }

✅ Good:

DoSomething();

EX008: ThreadAbortException must not be swallowed

Ensures ThreadAbortException is either rethrown or reset.

❌ Bad:

catch (ThreadAbortException ex) { LogError(); }

✅ Good:

catch (ThreadAbortException ex) { Thread.ResetAbort(); }

EX009: Empty try block with catch

Detects try blocks that are empty while having a catch.

❌ Bad:

try { } catch (Exception ex) { Log(ex); }

✅ Good:

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:

try { Task.WaitAll(tasks); } catch (Exception ex) { Log(ex); }

✅ Good:

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:

try { DoSomething(); } catch (Exception) { }

✅ Good:

try { DoSomething(); } catch (Exception) { /* intentionally ignored */ }

EX012: Don't throw exceptions from property getters

Discourages throwing exceptions directly from property getters.

❌ Bad:

public string Name => throw new Exception();

✅ Good:

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:

catch (Exception ex) { throw ex.InnerException; }

✅ Good:

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:

LogError(ex.Message);

✅ Good:

LogError(ex);

EX015: Avoid logging ex.ToString()

Recommends logging the exception directly rather than calling ToString.

❌ Bad:

LogError("Error: " + ex.ToString());

✅ Good:

LogError(ex);

EX016: Avoid empty catch when throwing new exception without message

Detects cases where a new exception is thrown in a catch block without message or inner exception.

❌ Bad:

catch (Exception ex) { throw new Exception(); }

✅ Good:

catch (Exception ex) { throw new Exception("Something went wrong", ex); }

EX017: Avoid when clauses that always evaluate to true

Detects when filters on catch blocks that always return true and are thus redundant.

❌ Bad:

catch (Exception ex) when (true) { Handle(ex); }

✅ Good:

catch (Exception ex) when (ex is IOException) { Handle(ex); }

EX018: NotImplementedException left in code

Detects throw new NotImplementedException() left in methods or properties.

❌ Bad:

public void DoWork() => throw new NotImplementedException();

✅ Good:

public void DoWork() => ActualImplementation();

EX019: Avoid general catch-all without any handling

Detects general catch blocks that don’t include logging, rethrow, or even a comment.

❌ Bad:

try { ... } catch { }

✅ Good:

try { ... } catch { /* intentionally blank */ }

EX020: Exception class should be public

Ensures that exception types are declared public to be visible when thrown or caught across assemblies.

❌ Bad:

class CustomException : Exception
{
}

✅ Good:

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:

public class MyCustomException : Exception
{
}

✅ Good:

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:

public MyCustomException(string message) { }
public MyCustomException(string message, Exception inner) { }

✅ Good:

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:

public class MyCustomError : Exception
{
}

✅ Good:

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:

try { ... }
catch (StackOverflowException ex) { Log(ex); }

✅ Good:

try { ... }
catch (Exception ex) { Log(ex); }

Acknowledgments

  • Davide Bellone
  • Andrew Lock
  • Marc Jacobi

Sponsored by elmah.io.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2025 Microsoft