{"id":17067744,"url":"https://github.com/justinamiller/exception-guidelines","last_synced_at":"2025-09-06T02:34:10.682Z","repository":{"id":143244201,"uuid":"97829293","full_name":"justinamiller/Exception-guidelines","owner":"justinamiller","description":"Do’s and Don’ts for Exceptions guidelines","archived":false,"fork":false,"pushed_at":"2017-07-26T11:34:24.000Z","size":17,"stargazers_count":3,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-07T06:05:12.926Z","etag":null,"topics":["c-sharp","exception","exception-handling","exceptions","guideline","guidelines"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/justinamiller.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2017-07-20T11:48:00.000Z","updated_at":"2024-03-11T08:21:25.000Z","dependencies_parsed_at":"2023-04-10T09:01:52.771Z","dependency_job_id":null,"html_url":"https://github.com/justinamiller/Exception-guidelines","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/justinamiller/Exception-guidelines","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justinamiller%2FException-guidelines","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justinamiller%2FException-guidelines/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justinamiller%2FException-guidelines/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justinamiller%2FException-guidelines/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/justinamiller","download_url":"https://codeload.github.com/justinamiller/Exception-guidelines/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justinamiller%2FException-guidelines/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273848619,"owners_count":25178955,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-06T02:00:13.247Z","response_time":2576,"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":["c-sharp","exception","exception-handling","exceptions","guideline","guidelines"],"created_at":"2024-10-14T11:11:32.172Z","updated_at":"2025-09-06T02:34:10.645Z","avatar_url":"https://github.com/justinamiller.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Exception-guidelines\n\n### When Should You Log Exceptions?\nThe short and sweet answer is that you should always log exceptions. For many developers, the general rule of thumb is to log exceptions when code could crash. The problem with this approach, however, is that even the simplest code can cause crashes in the application.\n\n### Top five tips for handling .NET Exceptions.\n\n***1. Maintain your stack trace***\n\nWhen you first start programming in .NET you might think that you catch the exception, try some stuff but rethrow that exception if you couldn’t handle it.  Or, you may have defined your own exception class.  It might have looked like the following code.\n\nIn both these cases, you will find that the stack trace provided with your exception will go no further down the call stack than the method PartialStackTrace.\n````\nprivate double PartialStackTrace()\n{\n  try\n  {\n       return DivideANumber(1, 0);\n  }\n  catch (Exception ex)\n  {\n       throw ex;\n  }\n}\n\nOR\n\nprivate double PartialStackTrace()\n{\n  try\n  {\n       return DivideANumber(1, 0);\n  }\n  catch (Exception ex)\n  {\n       throw new MyAppException(“Ooops!”);\n  }\n}\n````\nYou really would like a full stack trace, that reveals that the problem exists in DivideANumber (or further down the call stack).\n\nIf you are just re-throwing the exception use throw by itself, and the full stack trace is preserved.\n\nIf you define your own exception class, provide the exception you just caught as the InnerException in the constructor.  Job done!\n````\nprivate double FullStackTrace()\n{\n  try\n  {\n       return DivideANumber(1, 0);\n  }\n  catch (Exception ex)\n  {\n       throw;\n  }\n}\n\nOR\n\nprivate double FullStackTrace()\n{\n  try\n  {\n       return DivideANumber(1, 0);\n  }\n  catch (Exception ex)\n  {\n       throw new MyAppException(“Ooops!”, ex);\n  }\n}\n````\n\n***2. Keep exceptions exceptional…***\n\nThis sounds a bit trite, but sometimes you won’t need exceptions as you know there will be times that you will receive data or be in a situation where an exception might occur, and you know how to handle it.\n\nAs an example, it is trivial to verify that a key exists in a dictionary prior to attempting to access a value, and save having to wrap dictionary access in a try … catch block.\n\n````\nar alphabet = new Dictionary\u003cint, string\u003e() { { 1, \"A\" }, { 2, \"B\" } };\n\n// throws KeyNotFoundException\n\ntry\n{\n    Console.WriteLine($\"27th letter of alphabet {alphabet[27]}\");\n}\ncatch (KeyNotFoundException kex)\n{\n    Console.WriteLine(\"27th letter of alphabet : \u003cnot present\u003e\");\n}\n\n// check first, no try ... catch required\nvar letter = alphabet.ContainsKey(27) ? alphabet[27] : \"\u003cnot present\u003e\";\nConsole.WriteLine($\"27th letter of alphabet : { letter }\");k\n````\nSimilarly, you might want to check for the existence of a file with the same name before an attempt to copying a new file.  This could be gracefully handled by changing the resulting file name and providing the new file name back as a result.\n\n***3. Avoid generic, catch-all exception handling***\n\nNot only should you not throw exceptions for conditions that are expected to occur regularly, you should also always aim to catch specific exceptions, rather than generic exceptions.  This has become finer grained in C# 6 as it supports filtering using catch ... when and supplying a suitable predicate.\n\n````\n// handle specific exception differently\n\ntry\n{\n    double result = DivideByZeroCheckForSpecificException();\n    Console.WriteLine($\"Divide by zero results : {result}\\n\");\n}\ncatch (DivideByZeroException dex)\n{\n    Console.WriteLine(\"Divide by zero results : \u003ccannot divide by 0\u003e\\n\");\n}\ncatch (Exception ex)\n{\n    Console.WriteLine(\"Unknown exception : \\n\\n\" + ex.ToString() + \"\\n\");\n}\n\n// handle exception using a catch ... when predicate \n\ntry\n{\n    double result = DivideByZeroCheckExceptionPredicate();\n    Console.WriteLine($\"Divide by zero results : {result}\");\n}\ncatch (Exception ex) when (ex.InnerException != null)\n{\n    Console.WriteLine(\"An InnerException exists : \\n\\n\" + ex.ToString() + \"\\n\");\n}\n````\n\n***4. Async and exception handling***\n\nThe world of async has given us a few more things to think about regarding exceptions.  The first is that you will be expected to handle the AggregateException much more often than you may have done previously.  This wrapper could be wrapping several exceptions, or just a single exception.  You have to examine the InnerExceptions property to see if there are underlying exceptions which you could handle.\n\nIt should also be noted that methods which are decorated with async will not throw exceptions unless you await them.  This can be easy to miss when creating unit tests with mocks and stubs, and can lead to much frustration as tests suddenly fail due to exceptions not being thrown and handled as expected.\n\n````\ninternal static async Task ThrowAnException()\n{\n    throw new Exception(\"Throwing from an async decorated method.\");\n}\nprivate static async Task TestAsyncExceptions()\n{\n\n  // No exception will be thrown, we omitted await\n\n  try\n  {\n    ThrowAnException();\n    Console.WriteLine(\"No exception thrown.\");\n  }\n  catch (Exception ex)\n  {\n    Console.WriteLine(\"Exception thrown : \\n\\n\" + ex.ToString() + \"\\n\");\n  }\n\n  // Exception will be thrown because we have used await\n\n  try\n  {\n    await ThrowAnException();\n    Console.WriteLine(\"No exception thrown.\");\n  }\n  catch (Exception ex)\n  {\n    Console.WriteLine(\"Exception thrown : \\n\\n\" + ex.ToString() + \"\\n\");\n  } \n}\n````\n\n***5. Don’t rely on exception message content, it could be localised***\n\nEven when you do capture on specific exception types, you might find that the exception is ambiguous and doesn’t really inform you of the underlying issue.  It can be tempting to check for a given exception message to really confirm that the exception is something you think you can handle.\n\n````\ntry\n{  \n  getDocument = DocStore.SingleOrDefault(d =\u003e d.SelfLink = doc.SelfLink); \n}\ncatch (System.InvalidOperationException ex)\n{    \n  if (ex.Message == “Sequence contains more than one matching element”)  \n  {     \n    CleanUpDuplicates(doc.SelfLink);  \n  }  \n  else  \n  {    \n   throw;  \n  }\n}\n````\n\n\n### \"Do’s and Don’ts for Exceptions\"\n1. Don’t just wrap an entire method with one try-catch. Place try-catch around specific code.\n\n**Wrong**\n````\npublic ActionResult Index()\n{\n    try\n    {\n       // all of your code here\n\n    } catch (Exception e)\n   {\n      // do something\n   }\n}\n````\n\n**Right**\n````\npublic ActionResult Index()\n{\n  int myNumber1 = 0;\n  int myNumber2 = 1;\n    try\n    {\n       int result = DivideNumbers(3, 4);\n    } catch (Exception e)\n   {\n      // do something\n   }\n       try\n    {\n       int result2 = DivideNumbers(myNumber1, myNumber2);\n    } catch (Exception e)\n   {\n      // do something\n   }\n   \n   \n \n\n}\n````\n\n2. Do be descriptive. Your logs should contain specific information that helps you identify the root of an error. Don’t just log “error.” Log “Critical error in logging class” and then log the exception information.\n\n3. Don’t have an empty catch statement. In other words, don’t just “do nothing” with the caught exception. You should log the information and then either redirect the user or alert the user that something went wrong.\n\n4. Do inherit from the Exception class for custom exceptions. You can define your own exceptions for specific programming scenarios. For instance, you might want to define a specific error for your data layers to catch connection issues and bugs.\n\n5. Don’t throw errors that are actually not errors. For instance, if we query a database for a list of users and no users are returned, this isn’t an error. An empty data set is not an error even if logically it shouldn’t happen. Handle this type of scenario using logic workflow.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjustinamiller%2Fexception-guidelines","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjustinamiller%2Fexception-guidelines","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjustinamiller%2Fexception-guidelines/lists"}