
error-handling
Error handling strategy for .NET 10 applications. Covers the Result pattern, ProblemDetails (RFC 9457), global exception handling, FluentValidation, and structured error responses. Load this skill when implementing error handling, validation, or designing API error contracts, or when the user mentions "error handling", "Result pattern", "ProblemDetails", "exception", "validation", "FluentValidation", "error response", "global exception handler", or "RFC 9457".
- Installs
- 2.9K
- Last 24h
- +55
- Trending rank
- #3154
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/codewithmukesh/dotnet-claude-kit --skill error-handling- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads error-handling automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
Error Handling
Core Principles
- Use the Result pattern for expected failures — Don't throw exceptions for things like "order not found" or "validation failed". These are expected outcomes, not exceptional conditions. See ADR-002.
- Reserve exceptions for unexpected failures — Database connection lost, null reference bugs, network timeouts — these are truly exceptional and should propagate to the global handler.
- Every API error returns ProblemDetails — RFC 9457 is the standard. Every error response has
type,title,status,detail, and optionallyerrors. - Validate at the boundary — Validate incoming requests at the API layer, not deep inside business logic.
Patterns
Result Pattern
A simple, generic result type that carries either a value or errors.
public class Result
{
public bool IsSuccess { get; }
public bool IsFailure => !IsSuccess;
public List<string> Errors { get; }
protected Result(bool isSuccess, List<string>? errors = null)
{
IsSuccess = isSuccess;
Errors = errors ?? [];
}
public static Result Success() => new(true);
public static Result Failure(params string[] errors) => new(false, [..errors]);
public static Result<T> Success<T>(T value) => new(value);
public static Result<T> Failure<T>(params string[] errors) => new(errors);
}
public class Result<T> : Result
{
public T Value { get; }
internal Result(T value) : base(true) => Value = value;
internal Result(IEnumerable<string> errors) : base(false, [..errors]) => Value = default!;
}
Result to ProblemDetails Mapping
public static class ResultExtensions
{
public static IResult ToProblemDetails(this Result result, int statusCode = 400)
{
return TypedResults.Problem(
title: "One or more errors occurred",
statusCode: statusCode,
extensions: new Dictionary<string, object?>
{
["errors"] = result.Errors
});
}
}
// Usage in endpoint
group.MapPost("/", async (CreateOrder.Command command, ISender sender, CancellationToken ct) =>
{
var result = await sender.Send(command, ct);
return result.IsSuccess
? TypedResults.Created($"/api/orders/{result.Value.Id}", result.Value)
: result.ToProblemDetails();
});
Global Exception Handler
Catches unexpected exceptions and converts them to ProblemDetails. For the modern IExceptionHandler approach (preferred), see knowledge/common-infrastructure.md. The inline lambda below works for simple cases:
// Program.cs
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
var exception = context.Features.Get<IExceptionHandlerFeature>()?.Error;
var logger = context.RequestServices.GetRequiredService<ILogger<Program>>();
logger.LogError(exception, "Unhandled exception for {Method} {Path}",
context.Request.Method, context.Request.Path);
var problem = new ProblemDetails
{
Title = "An unexpected error occurred",
Status = StatusCodes.Status500InternalServerError,
Type = "https://tools.ietf.org/html/rfc9110#section-15.6.1"
};
// Don't leak details in production
if (context.RequestServices.GetRequiredService<IHostEnvironment>().IsDevelopment())
{
problem.Detail = exception?.Message;
}
context.Response.StatusCode = problem.Status.Value;
await context.Response.WriteAsJsonAsync(problem);
});
});
FluentValidation with Endpoint Filters
// Validator
public class CreateOrderValidator : AbstractValidator<CreateOrderRequest>
{
public CreateOrderValidator()
{
RuleFor(x => x.CustomerId)
.NotEmpty().WithMessage("Customer ID is required");
RuleFor(x => x.Items)
.NotEmpty().WithMessage("At least one item is required");
RuleForEach(x => x.Items).ChildRules(item =>
{
item.RuleFor(x => x.ProductId).NotEmpty();
item.RuleFor(x => x.Quantity).GreaterThan(0);
});
}
}
// Generic validation filter
public class ValidationFilter<TRequest> : IEndpointFilter
{
public async ValueTask<object?> InvokeAsync(
EndpointFilterInvocationContext context,
EndpointFilterDelegate next)
{
var validator = context.HttpContext.RequestServices.GetService<IValidator<TRequest>>();
if (validator is null)
return await next(context);
var request = context.Arguments.OfType<TRequest>().FirstOrDefault();
if (request is null)
return await next(context);
var result = await validator.ValidateAsync(request);
if (!result.IsValid)
{
return TypedResults.ValidationProblem(result.ToDictionary());
}
return await next(context);
}
}
// Registration
group.MapPost("/", CreateOrder)
.AddEndpointFilter<ValidationFilter<CreateOrderRequest>>();
Typed Error Results
For richer error handling, use typed error enums or error objects.
public abstract record Error(string Code, string Message);
public record NotFoundError(string Entity, object Id)
: Error("not_found", $"{Entity} with ID {Id} was not found");
public record ValidationError(string Field, string Message)
: Error("validation", Message);
public record ConflictError(string Message)
: Error("conflict", Message);
// Map to HTTP status codes
public static IResult ToHttpResult(this Error error) => error switch
{
NotFoundError => TypedResults.Problem(title: error.Message, statusCode: 404),
ValidationError => TypedResults.Problem(title: error.Message, statusCode: 400),
ConflictError => TypedResults.Problem(title: error.Message, statusCode: 409),
_ => TypedResults.Problem(title: error.Message, statusCode: 500)
};
Anti-patterns
Don't Throw Exceptions for Flow Control
// BAD — exceptions for expected outcomes
public Order GetOrder(Guid id)
{
var order = db.Orders.Find(id)
?? throw new NotFoundException($"Order {id} not found");
return order;
}
// GOOD — Result pattern
public Result<Order> GetOrder(Guid id)
{
var order = db.Orders.Find(id);
return order is not null
? Result.Success(order)
: Result.Failure<Order>($"Order {id} not found");
}
Don't Return Raw Error Strings from APIs
// BAD — inconsistent error format
return Results.BadRequest("Something went wrong");
return Results.BadRequest(new { error = "Invalid input" });
// GOOD — always ProblemDetails
return TypedResults.Problem(title: "Invalid input", statusCode: 400);
return TypedResults.ValidationProblem(validationResult.ToDictionary());
Don't Catch and Swallow Exceptions
// BAD — silently swallowing
try { await ProcessOrder(order); }
catch (Exception) { /* ignore */ }
// GOOD — log and handle appropriately
try { await ProcessOrder(order); }
catch (PaymentException ex)
{
logger.LogWarning(ex, "Payment failed for order {OrderId}", order.Id);
return Result.Failure<Order>("Payment processing failed");
}
Decision Guide
| Scenario | Recommendation |
|---|---|
| Expected business failure | Result pattern |
| Input validation | FluentValidation with endpoint filter |
| Unexpected crash | Global exception handler → ProblemDetails |
| API error format | RFC 9457 ProblemDetails — always |
| Validation in handler | Return Result.Failure, don't throw |
| External service failure | Catch specific exception, return Result.Failure |
| Logging errors | Structured logging with correlation ID |
FAQ
What does the error-handling skill do?
Error handling strategy for .NET 10 applications. Covers the Result pattern, ProblemDetails (RFC 9457), global exception handling, FluentValidation, and structured error responses. Load this skill when implementing error handling, validation, or designing API error contracts, or when the user mentions "error handling", "Result pattern", "ProblemDetails", "exception", "validation", "FluentValidation", "error response", "global exception handler", or "RFC 9457".
How do I install error-handling?
Run npx skills add https://github.com/codewithmukesh/dotnet-claude-kit --skill error-handling in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/error-handling/ (personal) or .claude/skills/error-handling/ (project).
Which AI agents can use error-handling?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is error-handling safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains error-handling?
It is published by codewithmukesh in codewithmukesh/dotnet-claude-kit. It has 2,895 recorded installs.
More from codewithmukesh/dotnet-claude-kit
clean-architecture
codewithmukesh/dotnet-claude-kit
Clean Architecture for .NET applications. Covers the 4-project layout (Domain, Application, Infrastructure, Api), dependency inversion, use case handlers, domain entities with behavior, and infrastructure as a plugin. Load this skill when building a project with Clean Architecture, discussing layered architecture, dependency inversion, use cases, or when the architecture-advisor recommends Clean Architecture.
ddd
codewithmukesh/dotnet-claude-kit
Domain-Driven Design tactical patterns for .NET applications. Covers aggregates, aggregate roots, value objects, domain events, domain services, strongly-typed IDs, and repository patterns for aggregate persistence. Load this skill when implementing DDD, working with aggregates, value objects, domain events, bounded contexts, or when the architecture-advisor recommends DDD + Clean Architecture. Pair with the clean-architecture skill.
modern-csharp
codewithmukesh/dotnet-claude-kit
Modern C language features for .NET 10 and C 14. Covers primary constructors, collection expressions, the field keyword, extension members, records, pattern matching, spans, and raw string literals. Load this skill when writing any new C code, reviewing existing code for modernization, using "modern C", "C 14", "primary constructor", "collection expression", "records", "pattern matching", "span", "field keyword", or "extension members". Always loaded as the baseline for all agents.
vertical-slice
codewithmukesh/dotnet-claude-kit
Vertical Slice Architecture (VSA) for .NET applications — one of several supported architectures in dotnet-claude-kit. Covers feature folders, endpoint grouping, and handler patterns for Mediator, Wolverine, and raw handler classes. Load this skill when the architecture-advisor recommends VSA, when working in an existing VSA codebase, when adding features to a feature-folder project, or when discussing vertical slice patterns, feature folders, or handler patterns.
security-scan
codewithmukesh/dotnet-claude-kit
Deep security scanning for .NET applications across 6 layers: vulnerable packages, secrets detection, OWASP code patterns, auth configuration, CORS policy, and data protection. Produces severity-rated findings with specific remediation steps. Load this skill when: "security scan", "security audit", "check for vulnerabilities", "find secrets", "OWASP", "auth review", "CORS check", "security review", "penetration test prep", "CVE check", "vulnerability scan", "hardcoded password", "data protection", "security posture".
serilog
codewithmukesh/dotnet-claude-kit
Structured logging with Serilog for .NET 10 applications. Covers two-stage bootstrap, appsettings configuration, enrichers, sinks, request logging, destructuring, and Serilog.Expressions. Load this skill when setting up Serilog, configuring log sinks, enrichers, or structured logging, or when the user mentions "Serilog", "structured logging", "log enrichment", "Seq", "LogContext", "UseSerilog", "WriteTo", "message template", "Serilog.Expressions", "request logging", "log sink", "rolling file", or "audit log".
Popular backend & apis skills
find-skills
vercel-labs/skills
Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.
entra-app-registration
microsoft/azure-skills
Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration. USE FOR: create app registration, register Azure AD app, configure OAuth, set up authentication, add API permissions, generate service principal, MSAL example, console app auth, Entra ID setup, Azure AD authentication. DO NOT USE FOR: Key Vault secrets (use azure-keyvault-expiration-audit), general Azure resource security guidance.
google-agents-cli-adk-code
google/agents-cli
This skill should be used when the user wants to "write agent code", "build an agent with ADK", "add a tool", "create a callback", "define an agent", "use state management" — in a project that needs ADK (Agent Development Kit) API patterns and code examples. It provides a quick reference for agent types, tool definitions, orchestration patterns, callbacks, state management, the graph Workflow API, and reference recipes to study. Do NOT use for scaffolding (use google-agents-cli-scaffold) or deployment (use google-agents-cli-deploy).
embedded-captions
heygen-com/hyperframes
Add captions or subtitles to an existing single-subject talking-head video without editing the footage. Use for plain verbatim captions, cinematic captions embedded behind the subject, VFX captions, “炸/特效/酷炫字幕,” or a named identity from the 35-style catalog. Route by visual identity, not by backend engine. The quiet anchor rail is the default; embed every word only when the user explicitly wants a fully cinematic treatment. The workflow runs locally end to end, including transcription and subject matting; split multi-shot footage before applying it.
entra-agent-id
microsoft/azure-skills
Provision Microsoft Entra Agent Identity Blueprints, BlueprintPrincipals, and per-instance Agent Identities via Microsoft Graph, and configure OAuth 2.0 token exchange (fmipath, OBO, cross-tenant) including the Microsoft Entra SDK for AgentID sidecar. USE FOR: Agent Identity Blueprint, BlueprintPrincipal, agent OAuth, fmipath token exchange, agent OBO, Workload Identity Federation for agents, polyglot agent auth, Microsoft.Identity.Web.AgentIdentities. DO NOT USE FOR: standard Entra app registration (use entra-app-registration), Microsoft Foundry agent authoring (use microsoft-foundry).
neon
neondatabase/agent-skills
Overview of Neon, a complete set of cloud backend primitives around Lakebase Postgres: Auth, Object Storage, Functions, the AI Gateway, and Realtime. Start here to choose Neon for undecided login, files, APIs, and LLM calls, set up the CLI or MCP server, and follow the branch-first workflow. Use when building an app or backend on Neon, or when "Neon" or "Lakebase Postgres" is mentioned. Child skill neon-postgres wins for an existing DATABASEURL, SQL, schema, inspect, or search. Child skill neon-auth wins for login, users, sessions, identity routing, and Managed Better Auth setup. Child skill neon-realtime wins for live queries in reactive apps and agents. Also use for object storage, S3, buckets, serverless functions, function triggers, cron, AI gateway, LLM calls, realtime, logs, Loki, Grafana, observability, postgres, database, backend, Claimable Neon, neon.new, or a no-signup database.