TheExceptionator.Analyzers 1.3.33

dotnet add package TheExceptionator.Analyzers --version 1.3.33
                    
NuGet\Install-Package TheExceptionator.Analyzers -Version 1.3.33
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TheExceptionator.Analyzers" Version="1.3.33">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TheExceptionator.Analyzers" Version="1.3.33" />
                    
Directory.Packages.props
<PackageReference Include="TheExceptionator.Analyzers">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TheExceptionator.Analyzers --version 1.3.33
                    
#r "nuget: TheExceptionator.Analyzers, 1.3.33"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TheExceptionator.Analyzers@1.3.33
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TheExceptionator.Analyzers&version=1.3.33
                    
Install as a Cake Addin
#tool nuget:?package=TheExceptionator.Analyzers&version=1.3.33
                    
Install as a Cake Tool

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 throwing null – use a specific exception type instead.

❌ Bad:

throw null;

✅ Good:

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:

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

✅ Good:

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:

try { ... }
catch (Exception ex)
{
    if (ex is ArgumentException)
        HandleArgument();
}

✅ Good:

try { ... }
catch (ArgumentException)
{
    HandleArgument();
}

EX019: 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();

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); }

EX025: Handle documented exceptions or propagate them explicitly

Flags calls to methods that document thrown exceptions (via <exception> XML comments) when the caller does not handle or document those exceptions.

❌ Bad:

public void Method1()
{
    Method2(); // Method2 documents that it may throw NullReferenceException
}

/// <summary>
/// <exception cref="System.NullReferenceException"/>
/// </summary>
public void Method2()
{
    var x = 1 / 0;
}

✅ Good:

public void Method1()
{
    try
    {
        Method2();
    }
    catch (NullReferenceException ex)
    {
        throw;
    }
}

/// <summary>
/// <exception cref="System.NullReferenceException"/>
/// </summary>
public void Method2()
{
    var x = 1 / 0;
}

EX026: Document all explicitly thrown exceptions when using <exception> tags

Flags methods that already document one or more exceptions using <exception> XML tags but forget to document additional exceptions that are explicitly thrown within the method body.

❌ Bad:

/// <summary>
/// Does something.
/// </summary>
/// <exception cref="System.InvalidOperationException"/>
public void M()
{
    throw new InvalidOperationException();
    throw new ArgumentException("arg"); // not documented
}

✅ Good:

/// <summary>
/// Does something.
/// </summary>
/// <exception cref="System.InvalidOperationException"/>
/// <exception cref="System.ArgumentException"/>
public void M()
{
    throw new InvalidOperationException();
    throw new ArgumentException("arg");
}

Acknowledgments


Sponsored by elmah.io.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.3.33 246 9/22/2026
1.3.32 2,372 11/20/2025
1.3.31 447 11/20/2025
1.3.30 447 11/19/2025
1.2.28 239 11/5/2025
1.2.27 236 11/5/2025
1.2.26 236 11/4/2025
1.1.25 244 9/11/2025
1.1.24 335 6/28/2025
1.1.22 192 6/27/2025
1.0.21 221 6/26/2025
1.0.17 222 6/26/2025