Exception Handling
Exceptions signal exceptional failure — not normal control flow.
Introduction
Exceptions signal exceptional failure — not normal control flow. try/catch/finally blocks handle errors; unhandled exceptions crash the process or return HTTP 500. ASP.NET Core middleware converts exceptions to ProblemDetails responses.
Specific catch order matters — most specific first. finally runs for cleanup regardless. Interviewers ask exception vs Result pattern, when not to catch, and performance cost of throw.
The story
A payment microservice calls an external charge API over HTTP. Network glitches, invalid requests, and user cancellations all produce different failures — the client wraps low-level HTTP errors into domain-friendly exceptions while preserving the original error for logs and re-throwing cooperative cancellations unchanged.
Understanding the topic
Key concepts
- try contains guarded code; catch handles types; finally always runs.
- Exception base class; specialized types ArgumentException, InvalidOperationException.
- throw; rethrows preserving stack trace.
- throw ex; resets stack — avoid.
- Filter catch when (condition) (.NET 6+).
- Global handlers: AppDomain.UnhandledException, ASP.NET exception middleware.
flowchart TDTry[try block] --> Error{Exception?}Error -->|yes| Catch[catch handler]Error -->|no| Finally[finally block]Catch --> FinallyFinally --> Continue[Resume or Rethrow]
Step-by-step explanation
- Runtime walks stack seeking matching catch.
- catch (Exception ex) logs and wraps or rethrows.
- finally disposes resources — prefer using statement.
- Async exceptions captured in Task — await propagates.
- AggregateException wraps parallel task failures.
- Exception filters evaluate before stack unwind.
Practical code example
Safe HTTP call with specific catches and exception filter:
namespace TechLearningPro.Exceptions;public sealed class PaymentClient(HttpClient http){public async Task<string> ChargeAsync(decimal amount, CancellationToken ct){try{var response = await http.PostAsync(quot;/charge?amount={amount}", null, ct);response.EnsureSuccessStatusCode();return await response.Content.ReadAsStringAsync(ct);}catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.BadRequest){throw new InvalidOperationException("Invalid charge request.", ex);}catch (TaskCanceledException) when (ct.IsCancellationRequested){throw; // cooperative cancel}}}
Line-by-line code explanation
PaymentClient(HttpClient http)receives an injected HTTP client fromIHttpClientFactory.try { ... }wraps the network call where failures are expected occasionally.await http.PostAsync(...)sends the charge request asynchronously.response.EnsureSuccessStatusCode()throws if the HTTP status indicates failure.return await response.Content.ReadAsStringAsync(ct)reads the success body on the happy path.catch (HttpRequestException ex) when (ex.StatusCode == BadRequest)catches only 400 errors via an exception filter.throw new InvalidOperationException("Invalid charge request.", ex)wraps the error with context and inner exception.catch (TaskCanceledException) when (ct.IsCancellationRequested)distinguishes user cancel from timeout.throw;rethrows without resetting the stack trace — correct for cooperative cancellation.
Key takeaway: Wrap low-level exceptions in domain-friendly types. when filter distinguishes timeout vs cancel. Preserve inner exception for diagnostics.
Real-world use
Where you'll use this in production
- API clients translating HTTP errors.
- Database retry on transient SqlException.
- Validation throwing ArgumentException on bad input.
- Global exception middleware logging correlation IDs.
Best practices
- Catch specific types; avoid bare catch unless top-level.
- Use using/await using for deterministic dispose.
- Log exception + context; never swallow silently.
- Prefer Try pattern for expected failures.
- Map exceptions to HTTP status in API layer only.
Common mistakes
- catch (Exception) { } empty swallow.
- throw ex; losing stack trace.
- Using exceptions for validation flow control.
- Catching ThreadAbortException (legacy).
Advanced interview questions
Q1Beginnerfinally without catch?
Q2Beginnerusing statement purpose?
Q3IntermediateException vs Result type?
Q4IntermediateAsync exception propagation?
Q5AdvancedDesign global error handling for ASP.NET Core API.
Summary
try/catch/finally handles exceptional failures. Catch specific types; preserve stack with throw; using and await using manage disposable resources. Map exceptions to HTTP at API boundary. Next: custom exception types.