Skip to content

Learn

An introduction to exception handling

In this post, you’ll learn the definition of an exception, what exception handling is, and why it’s so important, and some best practices when handling exceptions.

exception handling

Error handling is massively important in programming, to the point where some people say software development is mostly error handling. In many modern programming languages, the primary mechanism for managing errors is exception handling.

This guide is an introduction to exception handling. You’ll learn the definition of an exception, what exception handling is, and why it’s so important, and some best practices when handling exceptions. Additionally, we’ll show examples of exception handling in C#.

Before covering exception handling, though, it makes sense to offer a refresher on the concept of exceptions themselves.

Usually, the way an exception manifests itself during program execution is by interrupting the flow of execution

What is an exception?

An exception is a mechanism that many programming languages use to express a problem, error, or any unexpected situation.

Usually, the way an exception manifests itself during program execution is by interrupting the flow of execution. In other words, when an exception happens and it’s not handled, it crashes the program. Additionally, there is usually a message that accompanies the exception, with the goal of explaining what went wrong.

An exception is an event, which occurs during the execution of a program, that disrupts the normal flow of the program’s instructions.” – Java Tutorials, Oracle

Many modern programming languages—for instance, C#, Java, Python, and Ruby—model exceptions using classes. That is to say, they define one class to model a specific error or problem.

Taking C# as an example, these are common exception classes that represent common errors:

  • DivideByZeroException
  • NullReferenceException
  • FileNotFoundException
  • ArgumentOutOfRangeException

What is exception handling and why is it important in programming?

What is exception handling?

Exception handling is the practice of writing special code that is able to detect when an exception happens, get all of the available information about the exception, and (potentially) prevent the program from crashing.

Before we go any further, let’s introduce a bit of terminology: When an exception happens, we say the exception has been “thrown” or “raised.” When we handle the exception, we “catch” it.

The way this is usually done in practice is by surrounding the code that might throw an exception with special exception-handling code. If the exception happens, the normal flow of execution is interrupted right away and jumps to the exception-handling part.

What to do when handling an exception

So, what does it mean to handle an exception? You’ve caught an exception, but then what happens next?

There are a variety of things you can do after catching an exception, and the actions you’ll take depend on the specifics of your application. Here are some common tasks that are usually done after catching an exception:

Logging

A very common thing to do when catching an exception is to log as much detail as possible about the error, because that will enable someone in the future to troubleshoot and fix whatever went wrong.

Rethrow

Sometimes it makes sense to rethrow the exception. One common scenario is for the code to throw a nested exception, passing the original exception along as an inner exception, and then adding a more descriptive error message that will make more sense to whatever gets to handle it last.

Return an error code or result type

Another common scenario is this: You catch the exception, log the details, and then return some form of result type that indicates that the attempted operation failed somehow.

Try again

There are situations in which the error might be transient and, if you wait for a bit and try again, the code will later succeed. In these situations, you can use the exception-handling block to implement retry logic.

Resource cleanup

There are resources that need to be properly closed or disposed of. Think of things like database connections or file handlers, for instance. The exception-handling part is often a good area of the code for doing this, even though it’s not the only one—more on this later.

In any serious application, there’s a vast array of things that can go wrong

Why is exception handling important?

In any serious application, there’s a vast array of things that can go wrong. Applications handle, validate, and parse huge amounts of data. They interact with databases, third-party APIs, third-party packages, and libraries. They perform complex calculations. And generally speaking, they do a lot of tasks that might cause exceptions to happen one way or another.

Proper exception handling is essential to ensuring that your program is not only resilient against failures, but that it also degrades gracefully when they happen, providing friendly error messages to the end user and logging technical details to enable a technician/software engineer to troubleshoot and fix the issue.

A practical look at exception handling

Let’s now take a look at practical examples of exception handling in C#.

C# exception handling example #1

Let’s start with a simple console application that asks for a GitHub username and then shows some statistics about the entered user:

using Octokit; 
using ProductHeaderValue = Octokit.ProductHeaderValue; 
namespace ConsoleApp3; 

class Program 
{ 
static async Task Main() { var client = new GitHubClient(new ProductHeaderValue("MyApp")); 
Console.WriteLine("Enter a GitHub username:"); 
string? username = Console.ReadLine(); 
Console.WriteLine($"Getting user info for {username}...");
User user = await client.User.Get(username); 
Console.WriteLine($"API call successful! User: {user.Name} ({user.Login})"); 
Console.WriteLine($"Public repos: {user.PublicRepos}, Followers: {user.Followers}"); 
Console.WriteLine("Program finished."); } }
This is the result I get when I run the program. In the example below, I’m passing yyx990803< as the username, which is the username of Evan You, creator of the famous

Vue.js

frontend framework:
Enter a GitHub username: 
yyx990803 
Getting user info for yyx990803... 
API call successful! User: Evan You (yyx990803) 
Public repos: 198, Followers: 104825 
Program finished.
However, if I just enter some gibberish for the username, the code throws an exception:

 

More specifically, the code throws an exception of type Octokit.NotFoundException. That happens because what I typed isn’t a valid GitHub user. We need to handle that exception, so that we can show a friendly message when the entered user isn’t valid.

Let’s start by wrapping the offending code with a try block:

try 
{ 
Console.WriteLine($"Getting user info for {username}..."); 
User user = await client.User.Get(username); 
Console.WriteLine($"API call successful! User: {user.Name} ({user.Login})"); 
Console.WriteLine($"Public repos: {user.PublicRepos}, Followers: {user.Followers}");
Console.WriteLine("Program finished."); 
}

This is C#’s way of declaring that that portion of code might throw an exception. The next step is to add the catch block, in which we’ll handle the exception.

We know the specific exception type we’re looking for, so we’ll catch it explicitly:

catch (Octokit.NotFoundException ex) 
{
Console.WriteLine($"User not found: {ex.Message}"); 
}

The code above catches the exception and assigns the exception object to the ex variable. Then, it prints a friendly error message along with the exception message coming from the exception object.

The way this works is that if the code inside the try block throws an exception of type Octokit.NotFoundException, the control flow is immediately transferred to the catch block. If an exception of another type happens, the catch block won’t catch it.

If I run the code and enter some nonsense as the username once more, I’ll get a different result:

Enter a GitHub username: 
jkjgjhgjhg 
Getting user info for jkjgjhgjhg... 
User not found: Not Found

C# exception handling example #2

For this example, I’ll change the program:

static async Task Main() 
{ 
var configuration = new ConfigurationBuilder() 
.AddUserSecrets<Program>() 
.Build(); 
string gitHubToken = configuration.GetSection("GitHub:TOKEN").Value ?? "could not find token"; 
GitHubClient client = new(new ProductHeaderValue("MyApp")) 
{ 
Credentials = new Credentials(gitHubToken) 
}; 
var newRepo = new NewRepository("exception-handling-post"); 
await client.Repository.Create(newRepo); 
Console.WriteLine("Repository created successfully."); 
}

The code now does the following:

  1. Sets up a configuration from the user secrets .NET feature
  2. Gets a PAT (personal access token) from the configuration
  3. Instantiates a GitHub client object using the PAT for the credentials
  4. Creates a new repository object
  5. Tries to create a new repo on GitHub
  6. Displays a success message

When I run that code, I get an Octokit.AuthorizationException with the message “Bad credentials.” So, let’s add a try catch block for that possibility:

try 
{ 
string gitHubToken = configuration.GetSection("GitHub:TOKEN").Value ?? "could not find token"; 
GitHubClient client = new(new ProductHeaderValue("MyApp")) 
{ 
Credentials = new Credentials(gitHubToken) 
}; 
var newRepo = new NewRepository("exception-handling-post"); 
await client.Repository.Create(newRepo); 
Console.WriteLine("Repository created successfully."); 
} 
catch (Octokit.AuthorizationException ex) 
{ 
Console.WriteLine($"Something went wrong trying to authenticate to GitHub: {ex.Message}"); 
}

Now, when I run the code, I see this:

Something went wrong trying to authenticate to GitHub: Bad credentials

As it turns out, the code has a bug that needs to be fixed: I’m trying to retrieve the PAT from the configuration using the wrong key. Where it says GitHub:TOKEN it should’ve said GitHub:PAT.

Once that’s fixed, I start getting a different error:

Octokit.RepositoryExistsException: ‘There is already a repository named ‘exception-handling-post’ for the current account.’

That’s pretty self-explanatory and easy to fix. But let’s first handle the exception, adding yet another catch block:

catch (Octokit.RepositoryExistsException ex)
{
    Console.WriteLine($"Repository already exists: {ex.Message}");
}
Now, I can simply change the name of the repository to something that is guaranteed to be unique, and then we’ll no longer have errors.

Conclusion

This post was an introductory guide to exception handling, with examples in C#. You’ve learned what exceptions are, what exception handling is, why it matters, and how it works.

But there’s much more to it than that. As a next step, we encourage you to keep researching. Here are some topics you might be interested in:

  • How to create custom exception classes, and when you should do it
  • Best practices when logging exceptions
  • When to catch and when not to catch exceptions
  • The finally block in C#

As your project scales up, you can explore tools that will help you automate the validation of exception handling. Platforms like Tricentis Tosca can ensure that your applications correctly respond to both unexpected as well as expected errors. Thus, helping you catch unhandled exceptions early in the development or testing phase.

This post was written by Carlos Schults. Carlos is a skilled software engineer and an accomplished technical writer for various clients. His passion is to get to the bottom (the original source) of things and captivate readers with approachable and informative technical content.

Tricentis testing solutions

Learn how to supercharge your quality engineering journey with our advanced testing solutions.

Author:

Guest Contributors

Date: Nov. 05, 2025

Tricentis testing solutions

Learn how to supercharge your quality engineering journey with our advanced testing solutions.

Author:

Guest Contributors

Date: Nov. 05, 2025

You may also be interested in...