C# exception
last modified October 15, 2026
In this article we show how to work with exceptions in C#.
An exception represents an error that occurs during application execution.
During the execution of our application many things might go wrong. A disk might get full and we cannot save our file. An Internet connection might go down while our application tries to connect to a site. These errors might cause problems including an application crash. It is a responsibility of a programmer to handle errors that can be anticipated.
The try, catch and finally keywords are
used to work with exceptions.
Exception is the base class for all exceptions. The errors are
reported by exceptions being thrown. After an exception is thrown, it is handled
by the application or by the default exception handler. The exception contains
information about the error.
Different kinds of exceptions are thrown during the development of the application and after the development has finished. Developers receive exceptions with lots of technical details, while users get only short, basic informative messages.
C# catching exception
The catch keyword is used to catch an exception that is thrown.
int x = 100;
int y = 0;
int z;
try
{
z = x / y;
}
catch (ArithmeticException e)
{
Console.WriteLine("An exception occurred");
Console.WriteLine(e.Message);
}
In the above program, we intentionally divide a number by zero. This leads to an error. Note that this is a scholarly example to demonstrate how exceptions work. In reality, division by zero is a program error which is resolved by ensuring that the denominator is not zero.
try
{
z = x / y;
}
Statements that are error prone are placed in the try block.
catch (ArithmeticException e)
{
Console.WriteLine("An exception occurred");
Console.WriteLine(e.Message);
}
Exception types follow the catch keyword. In our case we have an
ArithmeticException. This exception is thrown for errors in an
arithmetic, casting, or conversion operation. The
DivideByZeroException, which is actually thrown, derives from
ArithmeticException; a catch block also catches
exceptions of derived types. Statements that follow the
catch keyword are executed when an error occurs. When an exception
occurs, an exception object is created. From this object we get the
Message property and print it to the console.
$ dotnet run An exception occurred Attempted to divide by zero.
C# uncaught exception
Any uncaught exception in the current context propagates to a higher context and looks for an appropriate catch block to handle it. If it can't find any suitable catch blocks, the default mechanism of the .NET runtime will terminate the execution of the entire program.
int x = 100; int y = 0; int z = x / y; Console.WriteLine(z);
In this program, we divide by zero. There is no custom exception handling.
$ dotnet run Unhandled exception. System.DivideByZeroException: Attempted to divide by zero. ...
The .NET runtime terminates the program and prints the above error message together with the stack trace.
C# IOException
The IOException is thrown when an I/O error occurs. In the
following example we read the contents of a file. The
FileNotFoundException and DirectoryNotFoundException
derive from IOException.
FileStream? fs = null;
try
{
fs = new FileStream("langs.txt", FileMode.Open);
var sr = new StreamReader(fs);
string? line;
while ((line = sr.ReadLine()) != null)
{
Console.WriteLine(line);
}
}
catch (IOException e)
{
Console.WriteLine("IO Error");
Console.WriteLine(e.Message);
}
finally
{
Console.WriteLine("Inside finally block");
fs?.Close();
}
The statements following the finally keyword are always executed.
It is often used for clean-up tasks, such as closing files or clearing buffers.
catch (IOException e)
{
Console.WriteLine("IO Error");
Console.WriteLine(e.Message);
}
In this case, we catch a specific IOException exception.
finally
{
Console.WriteLine("Inside finally block");
fs?.Close();
}
These lines guarantee that the file handle is closed. If the file could not be
opened, the fs variable is null; the null-conditional
operator ensures that we call Close only on an existing object. In
modern C#, we would rather use the using statement, which closes
the file automatically.
$ cat langs.txt C# Java Python Ruby PHP JavaScript
These are the contents of the langs.txt file.
$ dotnet run C# Java Python Ruby PHP JavaScript Inside finally block
The finally block is executed also when an exception occurs.
$ rm langs.txt $ dotnet run IO Error Could not find file '/home/jano/Documents/prog/csharp/exceptions/langs.txt'. Inside finally block
C# multiple exceptions
We often need to deal with multiple exceptions.
try
{
Console.Write("Enter first number: ");
int x = Convert.ToInt32(Console.ReadLine());
Console.Write("Enter second number: ");
int y = Convert.ToInt32(Console.ReadLine());
int z = x / y;
Console.WriteLine($"Result: {x} / {y} = {z}");
}
catch (DivideByZeroException e)
{
Console.WriteLine("Cannot divide by zero");
Console.WriteLine(e.Message);
}
catch (FormatException e)
{
Console.WriteLine("Wrong format of number.");
Console.WriteLine(e.Message);
}
In this example, we catch various exceptions. Note that more specific exceptions must precede the generic ones; otherwise, the compiler reports an error. We read two numbers from the console and check for zero division error and for wrong format of number.
$ dotnet run Enter first number: we Wrong format of number. The input string 'we' was not in a correct format. $ dotnet run Enter first number: 12 Enter second number: 0 Cannot divide by zero Attempted to divide by zero.
C# exception filters
An exception filter is a condition specified with the when keyword.
The catch block is executed only if the condition is true.
using System.Net;
int[] codes = [404, 500, 503];
foreach (var code in codes)
{
try
{
throw new HttpRequestException("Request failed", null, (HttpStatusCode) code);
}
catch (HttpRequestException e) when (e.StatusCode == HttpStatusCode.NotFound)
{
Console.WriteLine("Resource not found");
}
catch (HttpRequestException e) when ((int?) e.StatusCode >= 500)
{
Console.WriteLine($"Server error: {(int?) e.StatusCode}");
}
}
We simulate failed HTTP requests. Depending on the status code, the exception
is handled by different catch blocks.
$ dotnet run Resource not found Server error: 500 Server error: 503
C# rethrowing exceptions
Sometimes we want to do some work when an exception occurs, such as logging,
but let the caller handle the exception. For this, we use the
throw statement without an argument.
try
{
ReadConfig("config.json");
}
catch (FileNotFoundException e)
{
Console.WriteLine($"Caught in caller: {Path.GetFileName(e.FileName)}");
}
void ReadConfig(string fileName)
{
try
{
var content = File.ReadAllText(fileName);
Console.WriteLine(content);
}
catch (IOException e)
{
Console.WriteLine($"Logging error: {e.GetType().Name}");
throw;
}
}
The ReadConfig method logs the error and rethrows the exception.
Using throw; instead of throw e; preserves the
original stack trace.
$ dotnet run Logging error: FileNotFoundException Caught in caller: config.json
C# throw helpers
Methods should validate their arguments. .NET provides static throw helper methods that check a condition and throw the appropriate exception.
try
{
Console.WriteLine(GetArea(3, 4));
Console.WriteLine(GetArea(-3, 4));
}
catch (ArgumentOutOfRangeException e)
{
Console.WriteLine(e.Message);
}
try
{
PrintName(null);
}
catch (ArgumentNullException e)
{
Console.WriteLine(e.Message);
}
int GetArea(int width, int height)
{
ArgumentOutOfRangeException.ThrowIfNegative(width);
ArgumentOutOfRangeException.ThrowIfNegative(height);
return width * height;
}
void PrintName(string? name)
{
ArgumentNullException.ThrowIfNull(name);
Console.WriteLine(name);
}
We use the ThrowIfNegative and ThrowIfNull helper
methods. The name of the parameter is added to the error message automatically.
$ dotnet run
12
width ('-3') must be a non-negative value. (Parameter 'width')
Actual value was -3.
Value cannot be null. (Parameter 'name')
C# custom exception
Custom exceptions are user defined exception classes that derive from the
System.Exception class.
int x = 340004;
const int LIMIT = 333;
try
{
if (x > LIMIT)
{
throw new BigValueException("Exceeded the maximum value");
}
}
catch (BigValueException e)
{
Console.WriteLine(e.Message);
}
class BigValueException : Exception
{
public BigValueException(string msg) : base(msg) { }
}
We assume that we have a situation in which we cannot deal with big numbers.
class BigValueException : Exception
We have a BigValueException class. This class derives from the
built-in Exception class.
const int LIMIT = 333;
Numbers bigger than this constant are considered to be "big" by our program.
public BigValueException(string msg) : base(msg) {}
Inside the constructor, we call the parent's constructor. We pass the message to the parent.
if (x > LIMIT)
{
throw new BigValueException("Exceeded the maximum value");
}
If the value is bigger than the limit, we throw our custom exception. We give the exception a message "Exceeded the maximum value".
catch (BigValueException e)
{
Console.WriteLine(e.Message);
}
We catch the exception and print its message to the console.
$ dotnet run Exceeded the maximum value
Source
Exceptions and Exception Handling
In this article we worked with exceptions in C#.
Author
List all C# tutorials.