{"id":29085186,"url":"https://github.com/elmahio/exceptionator","last_synced_at":"2026-06-11T03:31:24.189Z","repository":{"id":301130681,"uuid":"1008250201","full_name":"elmahio/Exceptionator","owner":"elmahio","description":"Roslyn analyzers for improving exception handling in C# code.","archived":false,"fork":false,"pushed_at":"2025-11-20T17:49:23.000Z","size":6753,"stargazers_count":10,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-05-27T08:38:05.811Z","etag":null,"topics":["best-practices","exception","roslyn","roslyn-analyzer","roslyn-analyzers"],"latest_commit_sha":null,"homepage":"https://marketplace.visualstudio.com/items?itemName=elmahio.theexceptionator","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/elmahio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"custom":["https://elmah.io/","https://elmah.io/pricing/"]}},"created_at":"2025-06-25T08:57:58.000Z","updated_at":"2026-05-21T21:14:14.000Z","dependencies_parsed_at":"2025-06-25T10:35:47.455Z","dependency_job_id":null,"html_url":"https://github.com/elmahio/Exceptionator","commit_stats":null,"previous_names":["elmahio/exceptionator"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/elmahio/Exceptionator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elmahio%2FExceptionator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elmahio%2FExceptionator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elmahio%2FExceptionator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elmahio%2FExceptionator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elmahio","download_url":"https://codeload.github.com/elmahio/Exceptionator/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elmahio%2FExceptionator/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34181554,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["best-practices","exception","roslyn","roslyn-analyzer","roslyn-analyzers"],"created_at":"2025-06-27T22:30:57.057Z","updated_at":"2026-06-11T03:31:24.183Z","avatar_url":"https://github.com/elmahio.png","language":"C#","funding_links":["https://elmah.io/","https://elmah.io/pricing/"],"categories":[],"sub_categories":[],"readme":"# The Exceptionator\n\n![The Exceptionator](https://raw.githubusercontent.com/elmahio/Exceptionator/refs/heads/main/src/ExceptionAnalyzer.Vsix/icon.png)\n\nRoslyn analyzers for improving exception handling in C# code.\n\nThis 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.\n\n## Rules\n\n### EX001: Exception should include a message\n\nEnsures that exceptions are instantiated with a meaningful message.\n\n❌ Bad:\n```csharp\nthrow new InvalidOperationException();\n```\n\n✅ Good:\n```csharp\nthrow new InvalidOperationException(\"Operation failed due to ...\");\n```\n\n### EX002: Avoid throwing base exceptions\n\nAvoids throwing base exceptions like System.Exception or System.SystemException.\n\n❌ Bad:\n```csharp\nthrow new Exception(\"Something went wrong\");\n```\n\n✅ Good:\n```csharp\nthrow new InvalidOperationException(\"Something went wrong\");\n```\n\n### EX003: Missing inner exception\n\nEnsures that newly thrown exceptions inside catch blocks include the original exception as inner exception.\n\n❌ Bad:\n```csharp\ncatch (Exception ex)\n{\n    throw new CustomException(\"Something failed\");\n}\n```\n\n✅ Good:\n```csharp\ncatch (Exception ex)\n{\n    throw new CustomException(\"Something failed\", ex);\n}\n```\n\n### EX004: Use 'throw;' instead of 'throw ex;'\n\nPreserves the original stack trace when rethrowing exceptions.\n\n❌ Bad:\n```csharp\ncatch (Exception ex)\n{\n    throw ex;\n}\n```\n\n✅ Good:\n```csharp\ncatch (Exception ex)\n{\n    throw;\n}\n```\n\n### EX005: Exception variable is unused\n\nDetects catch variables that are never used.\n\n❌ Bad:\n```csharp\ncatch (Exception ex) { LogError(); }\n```\n\n✅ Good:\n```csharp\ncatch (Exception ex) { LogError(ex); }\n```\n\n### EX006: Unreachable code after throw\n\nDetects code written after a throw statement that will never execute.\n\n❌ Bad:\n```csharp\nthrow new Exception();\nDoSomething();\n```\n\n✅ Good:\n```csharp\nDoSomething();\nthrow new Exception();\n```\n\n### EX007: Pointless try/catch block\n\nDetects try/catch blocks that don't add meaningful handling logic.\n\n❌ Bad:\n```csharp\ntry\n{\n    DoSomething();\n}\ncatch (Exception)\n{\n    throw;\n}\n```\n\n✅ Good:\n```csharp\nDoSomething();\n```\n\n### EX008: ThreadAbortException must not be swallowed\n\nEnsures ThreadAbortException is either rethrown or reset.\n\n❌ Bad:\n```csharp\ncatch (ThreadAbortException ex) { LogError(); }\n```\n\n✅ Good:\n```csharp\ncatch (ThreadAbortException ex) { Thread.ResetAbort(); }\n```\n\n### EX009: Empty try block with catch\n\nDetects try blocks that are empty while having a catch.\n\n❌ Bad:\n```csharp\ntry { } catch (Exception ex) { Log(ex); }\n```\n\n✅ Good:\n```csharp\ntry { DoSomething(); } catch (Exception ex) { Log(ex); }\n```\n\n### EX010: Task.WaitAll should be wrapped with AggregateException catch\n\nEnsures proper exception handling for Task.WaitAll by requiring AggregateException catch or using Task.WhenAll.\n\n❌ Bad:\n```csharp\ntry { Task.WaitAll(tasks); } catch (Exception ex) { Log(ex); }\n```\n\n✅ Good:\n```csharp\ntry { Task.WaitAll(tasks); } catch (AggregateException ex) { Log(ex); }\n```\n\n### EX011: Empty catch block\n\nDetects catch blocks that are completely empty without even a comment.\n\n❌ Bad:\n```csharp\ntry { DoSomething(); } catch (Exception) { }\n```\n\n✅ Good:\n```csharp\ntry { DoSomething(); } catch (Exception) { /* intentionally ignored */ }\n```\n\n### EX012: Don't throw exceptions from property getters\n\nDiscourages throwing exceptions directly from property getters.\n\n❌ Bad:\n```csharp\npublic string Name =\u003e throw new Exception();\n```\n\n✅ Good:\n```csharp\npublic string Name =\u003e _name ?? \"\";\n```\n\n### EX013: Avoid throwing ex.InnerException\n\nDetects when ex.InnerException is thrown directly, which may cause null reference issues and loses the stack trace.\n\n❌ Bad:\n```csharp\ncatch (Exception ex) { throw ex.InnerException; }\n```\n\n✅ Good:\n```csharp\ncatch (Exception ex) { throw; }\n```\n\n### EX014: Avoid logging only ex.Message\n\nSuggests logging the full exception instead of just the message to retain full context.\n\n❌ Bad:\n```csharp\nLogError(ex.Message);\n```\n\n✅ Good:\n```csharp\nLogError(ex);\n```\n\n### EX015: Avoid logging ex.ToString()\n\nRecommends logging the exception directly rather than calling ToString.\n\n❌ Bad:\n```csharp\nLogError(\"Error: \" + ex.ToString());\n```\n\n✅ Good:\n```csharp\nLogError(ex);\n```\n\n### EX016: Avoid throwing null – use a specific exception type instead.\n\n❌ Bad:\n```csharp\nthrow null;\n```\n\n✅ Good:\n```csharp\nthrow new InvalidOperationException(\"An error\");\n```\n\n### EX017: Avoid when clauses that always evaluate to true\n\nDetects `when` filters on catch blocks that always return true and are thus redundant.\n\n❌ Bad:\n```csharp\ncatch (Exception ex) when (true) { Handle(ex); }\n```\n\n✅ Good:\n```csharp\ncatch (Exception ex) when (ex is IOException) { Handle(ex); }\n```\n\n### EX018: Filter exceptions manually inside catch\n\nDetects 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.\n\n❌ Bad:\n```csharp\ntry { ... }\ncatch (Exception ex)\n{\n    if (ex is ArgumentException)\n        HandleArgument();\n}\n```\n\n✅ Good:\n```csharp\ntry { ... }\ncatch (ArgumentException)\n{\n    HandleArgument();\n}\n```\n\n### EX019: NotImplementedException left in code\n\nDetects `throw new NotImplementedException()` left in methods or properties.\n\n❌ Bad:\n```csharp\npublic void DoWork() =\u003e throw new NotImplementedException();\n```\n\n✅ Good:\n```csharp\npublic void DoWork() =\u003e ActualImplementation();\n```\n\n### EX020: Exception class should be public\n\nEnsures that exception types are declared `public` to be visible when thrown or caught across assemblies.\n\n❌ Bad:\n```csharp\nclass CustomException : Exception\n{\n}\n```\n\n✅ Good:\n```csharp\npublic class CustomException : Exception\n{\n}\n```\n\n### EX021: Missing expected constructors on custom exception\n\nEnsures that custom exceptions implement the expected constructors with message and inner exception parameters.\n\n❌ Bad:\n```csharp\npublic class MyCustomException : Exception\n{\n}\n```\n\n✅ Good:\n```csharp\npublic class MyCustomException : Exception\n{\n    public MyCustomException(string message) : base(message) { }\n\n    public MyCustomException(string message, Exception innerException)\n        : base(message, innerException) { }\n}\n```\n\n### EX022: Exception constructors must call base\n\nEnsures that exception constructors pass their parameters (message, innerException) to the base constructor.\n\n❌ Bad:\n```csharp\npublic MyCustomException(string message) { }\npublic MyCustomException(string message, Exception inner) { }\n```\n\n✅ Good:\n```csharp\npublic MyCustomException(string message) : base(message) { }\npublic MyCustomException(string message, Exception inner) : base(message, inner) { }\n```\n\n### EX023: Exception class name must end with 'Exception'\n\nEnsures consistency and clarity by requiring exception classes to follow the naming convention of ending with 'Exception'.\n\n❌ Bad:\n```csharp\npublic class MyCustomError : Exception\n{\n}\n```\n\n✅ Good:\n```csharp\npublic class MyCustomException : Exception\n{\n}\n```\n\n### EX024: Avoid catching fatal exceptions like StackOverflowException or ExecutionEngineException\n\nFlags catch blocks that handle fatal exceptions which should not be caught or are uncatchable.\n\n❌ Bad:\n```csharp\ntry { ... }\ncatch (StackOverflowException ex) { Log(ex); }\n```\n\n✅ Good:\n```csharp\ntry { ... }\ncatch (Exception ex) { Log(ex); }\n```\n\n### EX025: Handle documented exceptions or propagate them explicitly\n\nFlags calls to methods that document thrown exceptions (via `\u003cexception\u003e` XML comments) when the caller does not handle or document those exceptions.\n\n❌ Bad:\n```csharp\npublic void Method1()\n{\n    Method2(); // Method2 documents that it may throw NullReferenceException\n}\n\n/// \u003csummary\u003e\n/// \u003cexception cref=\"System.NullReferenceException\"/\u003e\n/// \u003c/summary\u003e\npublic void Method2()\n{\n    var x = 1 / 0;\n}\n```\n\n✅ Good:\n```csharp\npublic void Method1()\n{\n    try\n    {\n        Method2();\n    }\n    catch (NullReferenceException ex)\n    {\n        throw;\n    }\n}\n\n/// \u003csummary\u003e\n/// \u003cexception cref=\"System.NullReferenceException\"/\u003e\n/// \u003c/summary\u003e\npublic void Method2()\n{\n    var x = 1 / 0;\n}\n```\n\n### EX026: Document all explicitly thrown exceptions when using `\u003cexception\u003e` tags\n\nFlags methods that already document one or more exceptions using `\u003cexception\u003e` XML tags but forget to document additional exceptions that are explicitly thrown within the method body.\n\n❌ Bad:\n```csharp\n/// \u003csummary\u003e\n/// Does something.\n/// \u003c/summary\u003e\n/// \u003cexception cref=\"System.InvalidOperationException\"/\u003e\npublic void M()\n{\n    throw new InvalidOperationException();\n    throw new ArgumentException(\"arg\"); // not documented\n}\n```\n\n✅ Good:\n```csharp\n/// \u003csummary\u003e\n/// Does something.\n/// \u003c/summary\u003e\n/// \u003cexception cref=\"System.InvalidOperationException\"/\u003e\n/// \u003cexception cref=\"System.ArgumentException\"/\u003e\npublic void M()\n{\n    throw new InvalidOperationException();\n    throw new ArgumentException(\"arg\");\n}\n```\n\n## Acknowledgments\n\n* [Davide Bellone](https://github.com/bellons91)\n* [Andrew Lock](https://github.com/andrewlock)\n* [Marc Jacobi](https://github.com/obiwanjacobi)\n\n---\n\nSponsored by [elmah.io](https://elmah.io).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felmahio%2Fexceptionator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felmahio%2Fexceptionator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felmahio%2Fexceptionator/lists"}