🎯 Introduction
In real-world .NET Core applications — from Web APIs to microservices — exceptions are inevitable.
What matters most is how you handle them gracefully so your app never crashes unexpectedly and users get meaningful error responses.
This article covers all ways to handle exceptions in .NET Core, including:
Try–Catch–Finally
Global Exception Handling
Middleware-based handling
Exception Filters
Logging & Custom Exceptions
Best Practices with Real-World Scenarios
🧠 1. What is an Exception in .NET Core?
An exception is an unexpected runtime error that interrupts the normal execution flow.
For example:
int x = int.Parse("abc"); // FormatExceptionHere, trying to convert a non-numeric string to an integer causes a FormatException.
⚙️ 2. Basic Exception Handling — try, catch, finally
This is the most common and simplest way.
🧩 Example:
try
{
int[] numbers = { 1, 2, 3 };
Console.WriteLine(numbers[5]); // IndexOutOfRangeException
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
finally
{
Console.WriteLine("Execution completed.");
}
🔍 Explanation
try: Code that might throw an exception.catch: Handles the exception.finally: Always executes, even if an exception occurs (commonly used for cleanup, like closing DB connections).
🧩 3. Multiple Catch Blocks
You can handle different exceptions differently.
try
{
string value = null;
Console.WriteLine(value.Length); // NullReferenceException
}
catch (NullReferenceException ex)
{
Console.WriteLine("Null reference error!");
}
catch (Exception ex)
{
Console.WriteLine("General exception: " + ex.Message);
}
✅ Always keep the most specific exceptions first, and the generic Exception last.
🧱 4. Nested Try–Catch Blocks
Sometimes, you might handle exceptions differently in inner blocks.
try
{
try
{
File.ReadAllText("data.txt");
}
catch (FileNotFoundException)
{
Console.WriteLine("File missing. Creating a new one...");
File.WriteAllText("data.txt", "Sample content");
}
}
catch (Exception ex)
{
Console.WriteLine("Unexpected error: " + ex.Message);
}
💥 5. Throwing Exceptions Manually (throw)
You can explicitly raise exceptions using throw.
void ValidateAge(int age)
{
if (age < 18)
throw new ArgumentException("Age must be 18 or above.");
}
try
{
ValidateAge(15);
}
catch (ArgumentException ex)
{
Console.WriteLine(ex.Message);
}
👉 Useful for business validation logic.
🧰 6. Custom Exceptions
You can create your own exception classes for domain-specific errors.
🧩 Example:
public class InsufficientBalanceException : Exception
{
public InsufficientBalanceException(string message)
: base(message) { }
}
public class BankAccount
{
public decimal Balance { get; private set; } = 1000;
public void Withdraw(decimal amount)
{
if (amount > Balance)
throw new InsufficientBalanceException("Not enough funds.");
Balance -= amount;
}
}
💡 Usage
try
{
var account = new BankAccount();
account.Withdraw(2000);
}
catch (InsufficientBalanceException ex)
{
Console.WriteLine(ex.Message);
}
✅ Helps distinguish business exceptions from system exceptions.
🌐 7. Global Exception Handling in .NET Core Web API
In web apps, handling exceptions globally is crucial for clean and consistent error responses.
🧩 Example: Using UseExceptionHandler in Program.cs
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseExceptionHandler("/error");
app.Map("/error", (HttpContext context) =>
{
var exception = context.Features.Get<IExceptionHandlerFeature>()?.Error;
return Results.Problem(detail: exception?.Message, title: "Server Error");
});
app.MapGet("/divide", () => 10 / 0); // Will trigger global handler
app.Run();
🔍 Explanation
UseExceptionHandlercatches all unhandled exceptions.Redirects to
/errorendpoint.You can log and return structured error details (
ProblemDetails).
🧱 8. Custom Exception Middleware
For more control, you can create a custom middleware.
🧩 Middleware Example
public class ExceptionMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ExceptionMiddleware> _logger;
public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
_logger.LogError(ex, "An unhandled exception occurred.");
context.Response.StatusCode = 500;
await context.Response.WriteAsJsonAsync(new
{
StatusCode = 500,
Message = "Internal Server Error",
Detail = ex.Message
});
}
}
}
💡 Register Middleware
app.UseMiddleware<ExceptionMiddleware>();
✅ This approach is ideal for microservices and APIs — you can centralize all exception handling, logging, and custom responses.
⚖️ 9. Exception Filters (For MVC / Web API)
You can handle exceptions using filters.

Join the conversation! Your thoughts help the community grow.