error-handling
Skill0 starsChanged 32 days agoMITView on GitHub
Add and improve error handling in web applications and APIs. Use when the user asks to improve error handling, add a global error handler, create custom error classes, fix unhandled promise rejections, return consistent error responses, stop leaking stack traces to clients, handle async errors, or make errors easier to debug in production.
80 other projects use a version of this, 2 of them edited.
Adopted by
Most recently taken up by
- codewithmukesh/dotnet-claude-kittheir own version, first seen Nov 8, 2026
- lhbsaa/apex-unifiedtheir own version, first seen Nov 8, 2026
- ahmadvahidi/skill-atlastheir own version, first seen Nov 8, 2026
- nisarth/claude-skills-storetheir own version, first seen Nov 8, 2026
- AndersonAndrad/skillstheir own version, first seen Nov 8, 2026
- AnyOrgName/php-skillstheir own version, first seen Nov 8, 2026
Used with
Projects with it also use convention-learner, opentelemetry and 5 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
codewithmukeshSkillconvention-learner
Detects and enforces project-specific coding conventions by analyzing existing codebase patterns. Learns naming conventions, folder structure, test organization, and coding style from the existing code. Load when: "conventions", "coding standards", "project patterns", "enforce style", "detect patterns", "learn conventions", "code consistency".
Also in 3 projects that use this
codewithmukeshSkillopentelemetry
OpenTelemetry observability for .NET 10 applications. Covers traces, metrics, and logs using the OpenTelemetry SDK with OTLP export. Includes custom ActivitySource, IMeterFactory metrics, resource configuration, and Aspire Dashboard integration. Load this skill when setting up distributed tracing, custom metrics, OTLP export, or when the user mentions "OpenTelemetry", "OTLP", "traces", "spans", "Activity", "ActivitySource", "metrics", "IMeterFactory", "Meter", "Counter", "Histogram", "Gauge", "telemetry", "observability", "distributed tracing", "OTEL", or "Aspire Dashboard".
Also in 3 projects that use this
MovGP0Skillmodern-csharp
Modern C# style guidance for Bendex and MicroSea code changes, including primary constructors, expression-bodied members, var usage, target-typed new, lambdas, and pattern matching.
Also in 3 projects that use this
MicrosoftSkillresilience
Apply resilience patterns (retry, backoff, circuit breaking) when writing or reviewing code that makes outbound HTTP/REST calls, database operations, or any I/O that can experience transient failures. Use this skill when: (1) writing new code that calls external services or APIs, (2) reviewing code that uses fetch, HTTP clients, or service-to-service calls, (3) adding queue consumers or workers that interact with remote systems, (4) debugging intermittent failures in production.
Also in 4 projects that use this
junhjangSkillconfiguration
Adding a new config file, adding a field, or wiring a config consumer. Enforces schema validation at startup (no permissive defaults), env-var indirection for secrets, layered config (defaults / per-env / operator-override), hot-reload explicit per field.
Also in 4 projects that use this
MicrosoftSkillauthentication
Wire this in whenever the app uses or connects to data — records, a database, entities, per-user data, row-level security — or when the user wants sign-in, accounts, login, or protected pages. Rayfin data is always accessed as an authenticated user, so **data implies auth**; only a static page over public data skips it. The starter ships as a no-auth "hello world" that previews with no backend, and the full Fabric auth scaffolding is already in the project — this skill wires it in: AuthProvider + bootstrapAuth in main.tsx, the route guard in App.tsx, the sign-in page, sign-out, and the env/deploy requirement. Triggers: auth, authentication, sign in, sign-in, login, log in, sign out, logout, account, user, identity, protect route, require login, gated page, data, database, records, per-user, row-level security, who is the current user, Fabric auth, Entra.
Also in 5 projects that use this
Other versions80 projects use a version of this.
Word for word · 0
None found.
Edited · 2
- HamzaSheikh768/Agents-Skills-Lap★ 313% same
- BigPapiCB/Universal-Claude-Skills★ 212% same
Wrote their own version · 78
- microsoft/data-formulator★ 17ksame name
- epicenter-md/epicenter★ 4.8ksame name
- EpicenterHQ/epicenter★ 4.8ksame name
- software-mansion/react-native-executorch★ 1.8ksame name
- CloudAI-X/claude-workflow★ 1.4ksame name
- CloudAI-X/claude-workflow-v2★ 1.4ksame name
- codewithmukesh/dotnet-claude-kit★ 808same name
- alchemyplatform/aa-sdk★ 321same name
- Resgrid/Core★ 238same name
- DevelopersGlobal/ai-agent-skills★ 216same name
68 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository68 other files it gives its agents.
- AGENTS.mdAGENTS.md
- .claude/CLAUDE.mdCLAUDE.md
- accessibilitySkill
- api-designSkill
- api-versioningSkill
- authSkill
- background-jobsSkill
- cachingSkill
- capacity-planningSkill
- changelogSkill
- chaos-engineeringSkill
- ci-cdSkill
- circuit-breakerSkill
- claude-best-practicesSkill
- code-commentsSkill
- code-hygieneSkill
- code-reviewSkill
- code-structureSkill
- commit-styleSkill
- concurrency-and-lockingSkill
- database-transactionsSkill
- data-consistencySkill
- debug-infra-startupSkill
- debug-sessionSkill
- demo-video-creatorSkill
- demo-video-script-creatorSkill
- dependency-injectionSkill
- dependency-updateSkill
- disk-cleanupSkill
- distributed-tracingSkill
- dockerSkill
- env-configSkill
- env-setupSkill
- event-driven-architectureSkill
- feature-flagsSkill
- file-storageSkill
- graceful-shutdownSkill
- idempotencySkill
- input-validationSkill
- jars-engineering-blogsmithSkill
- jars-workshop-builderSkill
- licensingSkill
- loggingSkill
- memory-and-allocationsSkill
- migrate-dbSkill
- monitoringSkill
- multi-tenancySkill
- notificationsSkill
- openapiSkill
- paginationSkill
- perf-profileSkill
- pr-descriptionSkill
- query-optimizationSkill
- rate-limitingSkill
- refactorSkill
- releaseSkill
- runbook-writerSkill
- searchSkill
- secrets-auditSkill
- simplifySkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
3 rules. 0 are common, 2 are distinctive, and 1 is very specific to this project.
0
Common
Most of the 64 edited versions we read keep these.
2
Distinctive
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 14
Read the existing error handling before adding to it: Check for a global handler, custom error classes, and existing response shapes.
- Distinctive: line 135
Every error response from the API must have the same shape so clients can handle errors generically:
- Very specific: line 152
Never return 500 Internal Server Error with the actual exception message.
“Common” is measured: kept by at least 6 in 10 of the 64 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Error handling
Handle errors at the right layer, expose the right amount of detail to the client, and always give operators enough context to diagnose what went wrong. Never swallow errors silently and never leak internal details to users.
Ground rules
- Read the existing error handling before adding to it. Check for a global handler, custom error classes, and existing response shapes. Don't introduce a second pattern.
- Never swallow errors silently. A bare
catch {}orexcept: passthat does nothing is always wrong. At minimum, log and re-throw. - Never send stack traces or internal error messages to the client. Log them server-side; return a safe, generic message to the caller.
- Distinguish operational errors from programmer errors. Operational errors (invalid input, not found, auth failure) are expected and handled gracefully. Programmer errors (null dereference, assertion failure) should crash the process or alert — not be silently caught.
- Fail loudly in development, gracefully in production.
Step 0: Read the existing setup
# Find existing error classes or handlers
grep -r "class.*Error\|ErrorHandler\|error.*middleware\|app\.use.*err\|exception_handler\|HTTPException" \
--include="*.ts" --include="*.js" --include="*.py" --include="*.go" -l | head -10
# Find swallowed errors
grep -rn "catch\s*{}\|catch.*{\s*}\|except:\s*pass\|except Exception:\s*$" \
--include="*.ts" --include="*.js" --include="*.py" | head -20Custom error classes
Define a hierarchy once. Every thrown error should be an instance of a known class so handlers can make decisions without parsing message strings.
Node.js / TypeScript
// Base class — all app errors extend this
export class AppError extends Error {
constructor(
public readonly message: string,
public readonly statusCode: number,
public readonly code: string, // machine-readable, e.g. "USER_NOT_FOUND"
public readonly isOperational = true // false = programmer error → crash
) {
super(message);
this.name = this.constructor.name;
Error.captureStackTrace(this, this.constructor);
}
}
// Specific error types
export class NotFoundError extends AppError {
constructor(resource: string, id?: string) {
super(
id ? `${resource} '${id}' not found` : `${resource} not found`,
404,
"NOT_FOUND"
);
}
}
export class ValidationError extends AppError {
constructor(
message: string,
public readonly fields?: Record<string, string>
) {
super(message, 400, "VALIDATION_ERROR");
}
}
export class UnauthorizedError extends AppError {
constructor(message = "Authentication required") {
super(message, 401, "UNAUTHORIZED");
}
}
export class ForbiddenError extends AppError {
constructor(message = "Access denied") {
super(message, 403, "FORBIDDEN");
}
}
export class ConflictError extends AppError {
constructor(message: string) {
super(message, 409, "CONFLICT");
}
}Python
class AppError(Exception):
def __init__(self, message: str, status_code: int, code: str):
super().__init__(message)
self.status_code = status_code
self.code = code
class NotFoundError(AppError):
def __init__(self, resource: str, id: str | None = None):
msg = f"{resource} '{id}' not found" if id else f"{resource} not found"
super().__init__(msg, 404, "NOT_FOUND")
class ValidationError(AppError):
def __init__(self, message: str, fields: dict | None = None):
super().__init__(message, 400, "VALIDATION_ERROR")
self.fields = fields or {}
class UnauthorizedError(AppError):
def __init__(self, message: str = "Authentication required"):
super().__init__(message, 401, "UNAUTHORIZED")
class ForbiddenError(AppError):
def __init__(self, message: str = "Access denied"):
super().__init__(message, 403, "FORBIDDEN")Consistent error response shape
Every error response from the API must have the same shape so clients can handle errors generically:
{
"error": {
"code": "USER_NOT_FOUND",
"message": "User '42' not found",
"fields": { "email": "Email already in use" }
}
}code— machine-readable, stable across releases. Clients switch on this.message— human-readable, safe to display. No stack traces, no SQL, no file paths.fields— optional, for validation errors that map to specific inputs.
Never return 500 Internal Server Error with the actual exception message.
Global error handler
Express (TypeScript)
import { Request, Response, NextFunction } from "express";
import { AppError } from "./errors";
// Must have 4 parameters — Express identifies error handlers by arity
export function globalErrorHandler(
err: Error,
req: Request,
res: Response,
_next: NextFunction
) {
// Log everything server-side
req.log?.error({ err }, "Request failed") ?? console.error(err);
if (err instanceof AppError && err.isOperational) {
return res.status(err.statusCode).json({
error: {
code: err.code,
message: err.message,
...(err instanceof ValidationError && { fields: err.fields }),
},
});
}
// Programmer error or unknown — hide internals from client
return res.status(500).json({
error: {
code: "INTERNAL_ERROR",
message: "An unexpected error occurred.",
},
});
}
// Register last, after all routes
app.use(globalErrorHandler);FastAPI (Python)
from fastapi import Request
from fastapi.responses import JSONResponse
@app.exception_handler(AppError)
async def app_error_handler(request: Request, exc: AppError):
log.error("request.failed", path=request.url.path, error=str(exc), exc_info=True)
body = {"error": {"code": exc.code, "message": str(exc)}}
if isinstance(exc, ValidationError) and exc.fields:
body["error"]["fields"] = exc.fields
return JSONResponse(status_code=exc.status_code, content=body)
@app.exception_handler(Exception)
async def unhandled_exception_handler(request: Request, exc: Exception):
log.error("request.unhandled_error", path=request.url.path, exc_info=True)
return JSONResponse(
status_code=500,
content={"error": {"code": "INTERNAL_ERROR", "message": "An unexpected error occurred."}},
)Async error handling
In Node.js, unhandled promise rejections crash the process (Node 15+) or produce silent failures (older). Wrap async route handlers:
// Utility: wraps an async handler so Express catches rejections
export const asyncHandler =
(fn: (req: Request, res: Response, next: NextFunction) => Promise<void>) =>
(req: Request, res: Response, next: NextFunction) =>
fn(req, res, next).catch(next); // forwards to the global error handler
// Usage
router.get("/users/:id", asyncHandler(async (req, res) => {
const user = await userService.findById(req.params.id);
if (!user) throw new NotFoundError("User", req.params.id);
res.json(user);
}));Unhandled rejection safety net
// In your process entrypoint — catches anything that slips through
process.on("unhandledRejection", (reason) => {
console.error("Unhandled rejection:", reason);
// For programmer errors, crash and let the process manager restart
process.exit(1);
});
process.on("uncaughtException", (err) => {
console.error("Uncaught exception:", err);
process.exit(1);
});Where to throw vs where to catch
| Layer | Throw | Catch |
| Route handler | NotFoundError, ValidationError | Nothing — let the global handler catch |
| Service layer | ConflictError, ForbiddenError | External service errors (wrap and re-throw) |
| Repository / DB layer | Wrap DB errors into AppError subclasses | Raw DB driver errors |
| Global handler | — | Everything |
Catch at a layer only if you can add context or recover. Otherwise re-throw.
// Repository — translate DB errors into app errors
async function createUser(data: CreateUserInput) {
try {
return await db.user.create({ data });
} catch (err: any) {
if (err.code === "P2002") { // Prisma unique constraint violation
throw new ConflictError("A user with this email already exists");
}
throw err; // unknown error — let it bubble
}
}What not to do
// ❌ Swallowing — the error disappears silently
try {
await sendEmail(user.email);
} catch {}
// ❌ Logging without re-throwing — the request succeeds when it shouldn't
try {
await chargeCard(amount);
} catch (err) {
console.error(err);
// execution continues — order marked as paid when charge failed
}
// ❌ Returning the raw error message to the client
res.status(500).json({ error: err.message }); // may expose SQL, file paths, internals
// ❌ Using a generic Error when a specific class exists
throw new Error("not found"); // handler can't distinguish from a programmer error
// ✅ Correct: throw specific, catch at the boundary, log server-side, return safe shape
throw new NotFoundError("User", userId);Checklist
- Custom error class hierarchy defined (
AppErrorbase + specific subclasses) - Global error handler registered as the last middleware / exception handler
- All error responses use a consistent JSON shape (
code,message,fields) - Stack traces and internal messages never sent to the client
- Async route handlers wrapped or using framework-native async support
unhandledRejectionanduncaughtExceptionhandlers in the process entrypoint- DB / external service errors caught at the repository layer and wrapped into
AppError - No bare
catch {}orexcept: passblocks — every catch logs or re-throws - Operational errors (4xx) logged at WARN; programmer errors (5xx) logged at ERROR
- Error response for 500 returns
"An unexpected error occurred.", not the real message
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

