coder-csharp-error-handling

<skill_overview> Implement robust error handling following modern .NET patterns

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "coder-csharp-error-handling" with this command: npx skills add ozerohax/assistagents/ozerohax-assistagents-coder-csharp-error-handling

<skill_overview> Implement robust error handling following modern .NET patterns

Handling exceptions in services Implementing validation logic Designing API error responses Choosing Result pattern vs exceptions Setting up global exception handling

Microsoft Exception Handling

</skill_overview> <exceptions_vs_result> <use_exceptions_when> Truly exceptional, unexpected failures System-level errors (I/O, network, hardware) In constructors (can't return Result) Framework boundaries expecting exceptions Programming errors (null, out of range) </use_exceptions_when> <use_result_when> Expected business failures (validation, not found) Multiple possible failure modes Performance-critical hot paths Domain layer business logic Compile-time safety for error handling </use_result_when> <example_comparison>

// Exception: Unexpected failure if (connection == null) throw new InvalidOperationException("Connection not initialized"); // Result: Expected business outcome public Result<User> GetUser(int id) { var user = _repository.Find(id); if (user == null) return Result.Fail("User not found"); // Expected case return Result.Ok(user); }

</example_comparison> </exceptions_vs_result> <exception_best_practices>

Throw for truly exceptional conditions only Use specific exception types Include context in exception message

// Good: Specific exception with context throw new ArgumentNullException(nameof(email), "Email is required for user creation"); // Good: Use built-in guard methods (.NET 6+) ArgumentNullException.ThrowIfNull(user); ArgumentException.ThrowIfNullOrEmpty(email); // Bad: Generic exception throw new Exception("Error occurred");

Catch specific exceptions first, general last Use exception filters (when clause) for fine-grained control Always use "throw;" not "throw ex;" to preserve stack trace

try { await ProcessOrderAsync(order); } catch (ValidationException ex) { _logger.LogWarning(ex, "Validation failed for order {OrderId}", order.Id); return BadRequest(ex.Errors); } catch (NotFoundException ex) { return NotFound(ex.Message); } catch (Exception ex) when (ex is TimeoutException or HttpRequestException) { _logger.LogWarning(ex, "Transient error, will retry"); throw; // Preserve stack trace! } catch (Exception ex) { _logger.LogError(ex, "Unexpected error processing order {OrderId}", order.Id); throw; // Never "throw ex;" }

<custom_exceptions>

public class OrderProcessingException : Exception { public string OrderId { get; } public string ErrorCode { get; }

public OrderProcessingException(string orderId, string message, string errorCode) : base(message) { OrderId = orderId; ErrorCode = errorCode; }

public OrderProcessingException(string orderId, string message, Exception inner) : base(message, inner) { OrderId = orderId; }

}

</custom_exceptions> </exception_best_practices> <result_pattern>

Rich API, multiple errors, chaining Clean fluent API, functional style Discriminated unions, exhaustive matching

<erroror_example>

public async Task<ErrorOr<Order>> CreateOrderAsync(CreateOrderCommand cmd) { if (cmd.Items.Count == 0) return Error.Validation("Order must have items");

var customer = await _customerRepo.FindAsync(cmd.CustomerId); if (customer == null) return Error.NotFound("Customer not found");

if (!customer.IsActive) return Error.Validation("Customer account is inactive");

var order = new Order(cmd); await _orderRepo.AddAsync(order);

return order;

} // Controller usage [HttpPost] public async Task<IActionResult> CreateOrder(CreateOrderDto dto) { var result = await _orderService.CreateOrderAsync(dto.ToCommand());

return result.Match( order =&gt; Ok(new OrderDto(order)), errors =&gt; Problem(errors.First().Description) );

}

</erroror_example>

public async Task<ErrorOr<OrderResult>> ProcessOrderAsync(int orderId) { return await ValidateOrderAsync(orderId) .Then(order => CalculatePriceAsync(order)) .Then(order => ProcessPaymentAsync(order)) .Then(order => SendConfirmationAsync(order)); }

</result_pattern>

Powerful, fluent validation with separation of concerns

public class CreateUserValidator : AbstractValidator<CreateUserDto> { public CreateUserValidator(IUserRepository repository) { RuleFor(x => x.Email) .NotEmpty().WithMessage("Email is required") .EmailAddress().WithMessage("Invalid email format") .MustAsync(async (email, ct) => !await repository.ExistsAsync(email)) .WithMessage("Email already registered");

RuleFor(x =&#x26;gt; x.Password)
    .NotEmpty()
    .MinimumLength(8)
    .Matches("[A-Z]").WithMessage("Must contain uppercase")
    .Matches("[a-z]").WithMessage("Must contain lowercase")
    .Matches("[0-9]").WithMessage("Must contain number");

RuleFor(x =&#x26;gt; x.Age)
    .InclusiveBetween(18, 120);

}

}

<data_annotations> Simple validation with attributes <use_for>DTOs, simple input validation</use_for>

public class CreateUserDto { [Required] [EmailAddress] public string Email { get; init; }

[Required] [MinLength(8)] public string Password { get; init; }

[Range(18, 120)] public int Age { get; init; }

}

</data_annotations> <validation_layers> Input format validation (DataAnnotations) Business rules (FluentValidation) Invariants (guard clauses, exceptions) </validation_layers>

<aspnetcore_error_handling>

Global exception handling (.NET 8+)

public class GlobalExceptionHandler : IExceptionHandler { private readonly ILogger<GlobalExceptionHandler> _logger;

public GlobalExceptionHandler(ILogger&lt;GlobalExceptionHandler&gt; logger) { _logger = logger; }

public async ValueTask&lt;bool&gt; TryHandleAsync( HttpContext httpContext, Exception exception, CancellationToken ct) { _logger.LogError(exception, "Unhandled exception: {Message}", exception.Message);

var problemDetails = exception switch
{
    ValidationException ex =&#x26;gt; new ProblemDetails
    {
        Type = "https://example.com/validation-error",
        Title = "Validation Error",
        Status = 400,
        Detail = ex.Message
    },
    NotFoundException =&#x26;gt; new ProblemDetails
    {
        Type = "https://example.com/not-found",
        Title = "Not Found",
        Status = 404
    },
    _ =&#x26;gt; new ProblemDetails
    {
        Type = "https://example.com/internal-error",
        Title = "Internal Server Error",
        Status = 500,
        Detail = "An unexpected error occurred"
    }
};

problemDetails.Extensions["traceId"] = httpContext.TraceIdentifier;

httpContext.Response.StatusCode = problemDetails.Status ?? 500;
await httpContext.Response.WriteAsJsonAsync(problemDetails, ct);

return true;

}

} // Registration builder.Services.AddProblemDetails(); builder.Services.AddExceptionHandler<GlobalExceptionHandler>(); app.UseExceptionHandler();

<problem_details> RFC 7807 standard error format

{ "type": "https://example.com/validation-error", "title": "Validation Error", "status": 400, "detail": "Email is required", "instance": "/api/users", "traceId": "00-abc123..." }

</problem_details> </aspnetcore_error_handling> <logging_errors>

Always include exception object: LogError(ex, "message") Add context: orderId, userId, operation name Use structured logging with templates Include correlation ID for tracing

try { await ProcessOrderAsync(order); } catch (Exception ex) { _logger.LogError(ex, "Failed to process order {OrderId} for customer {CustomerId}. Amount: {Amount}", order.Id, order.CustomerId, order.TotalAmount); throw; }

<never_log> Passwords (even hashed) Credit card numbers API keys and tokens Personal identification numbers </never_log> </logging_errors> <anti_patterns>

catch (Exception) { } Swallows errors silently

catch (Exception ex) { throw new Exception("Error", ex); } Loses original exception type

throw ex; Resets stack trace throw;

Using exceptions for expected business logic Expensive, unclear intent Use Result pattern for expected failures

</anti_patterns>

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

coder-csharp-aspnetcore-api

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

coder-rust-async-concurrency

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

coder-csharp-efcore-queries

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

coder-rust-axum-api

No summary provided by upstream source.

Repository SourceNeeds Review