npx machina-cli add skill JanSzewczyk/claude-plugins/error-handling --openclawError Handling Skill
Comprehensive error handling patterns for Next.js applications.
Reference Files:
- patterns.md - Error handling patterns by layer
- examples.md - Practical code examples
- retry-patterns.md - Retry, circuit breaker, and resilience patterns
- validation-vs-runtime.md - Validation errors vs runtime errors guide
Error Handling Philosophy
Principles:
- Never expose internal errors to users - Log details server-side, show friendly messages client-side
- Use typed errors - DbError class for database, ActionResponse for server actions
- Fail gracefully - Error boundaries, fallback UI, retry mechanisms
- Log everything - Structured logging with context for debugging
- Provide feedback - Toast notifications for user-facing errors
Error Handling Layers
┌─────────────────────────────────────────────────────────────┐
│ Client Layer │
│ • Error Boundaries (React) │
│ • Toast Notifications (user feedback) │
│ • Form field errors (inline validation) │
└─────────────────────────────────────────────────────────────┘
↑
┌─────────────────────────────────────────────────────────────┐
│ Server Action Layer │
│ • ActionResponse<T> / RedirectAction types │
│ • Zod validation with fieldErrors │
│ • Toast cookies for redirect feedback │
└─────────────────────────────────────────────────────────────┘
↑
┌─────────────────────────────────────────────────────────────┐
│ Database Layer │
│ • DbError class (typed errors) │
│ • Tuple pattern [error, data] │
│ • categorizeDbError() helper │
└─────────────────────────────────────────────────────────────┘
↑
┌─────────────────────────────────────────────────────────────┐
│ Logging Layer │
│ • Pino structured logging │
│ • Error context (errorCode, isRetryable, userId) │
│ • Log before returning error │
└─────────────────────────────────────────────────────────────┘
Quick Reference
Database Layer (DbError)
import { categorizeDbError, DbError } from "~/lib/firebase/errors";
// Tuple pattern - always return [error, data]
export async function getById(
id: string,
): Promise<[null, Data] | [DbError, null]> {
if (!id?.trim()) {
return [DbError.validation("Invalid id"), null];
}
try {
const doc = await db.collection("items").doc(id).get();
if (!doc.exists) {
return [DbError.notFound("Item"), null];
}
return [null, transform(doc)];
} catch (error) {
const dbError = categorizeDbError(error, "Item");
logger.error({ errorCode: dbError.code, id }, "Database error");
return [dbError, null];
}
}
Server Action Layer
import { setToastCookie } from "~/lib/toast/server/toast.cookie";
import type { ActionResponse } from "~/lib/action-types";
export async function createItem(data: FormData): ActionResponse<Item> {
const { userId } = await auth();
if (!userId) {
return { success: false, error: "Unauthorized" };
}
// Validation errors - return fieldErrors for form display
const parsed = schema.safeParse(data);
if (!parsed.success) {
return {
success: false,
error: "Validation failed",
fieldErrors: parsed.error.flatten().fieldErrors,
};
}
// Database errors - toast + generic message
const [error, item] = await createItemInDb(parsed.data);
if (error) {
await setToastCookie("Failed to create item", "error");
return { success: false, error: "Unable to create item" };
}
await setToastCookie("Item created!", "success");
return { success: true, data: item };
}
Client Layer (Error Boundary)
// app/error.tsx - Catches unhandled errors
"use client";
export default function Error({
error,
reset
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
// Log to error tracking service
logError(error);
}, [error]);
return (
<div className="error-container">
<h2>Something went wrong</h2>
<Button onClick={reset}>Try again</Button>
</div>
);
}
DbError Properties
| Property | Type | Description |
|---|---|---|
code | string | Error code (validation, not-found, permission-denied, etc.) |
message | string | User-friendly error message |
isRetryable | boolean | True for transient errors (network, timeout) |
isNotFound | boolean | True when resource doesn't exist |
isAlreadyExists | boolean | True when creating existing resource |
isPermissionDenied | boolean | True for auth/permission issues |
Static Factory Methods
DbError.notFound("User"); // Resource not found
DbError.alreadyExists("Budget"); // Resource already exists
DbError.validation("Invalid input"); // Validation failed
DbError.dataCorruption("Event"); // Document exists but data invalid
DbError.permissionDenied(); // Auth/permission issue
Error Response Flow
Database Error
↓
categorizeDbError() → DbError with properties
↓
Log error with context (logger.error)
↓
Return tuple [error, null]
↓
Server Action checks error properties
↓
Set toast cookie for user feedback
↓
Return ActionResponse with generic message
↓
Client displays toast + handles state
Related Skills
firebase-firestore- DbError class and database patternsserver-actions- ActionResponse types and patternstoast-notifications- User feedback via toastsstructured-logging- Pino logging patterns
Source
git clone https://github.com/JanSzewczyk/claude-plugins/blob/main/plugins/nextjs-react/skills/error-handling/SKILL.mdView on GitHub Overview
This skill provides comprehensive error handling patterns across database, server actions, React UI, and logging in Next.js apps. It emphasizes typed errors (DbError, ActionResponse), user-facing feedback via toasts, and structured logging to aid debugging and resilience.
How This Skill Works
It defines four layers: Database Layer with DbError, the [error, data] tuple pattern, and categorizeDbError; Server Action Layer with ActionResponse/RedirectAction, Zod fieldErrors, and toast cookies for redirects; Client Layer with React Error Boundaries and toast notifications; and a Logging Layer using Pino for structured logs with error context, logged before returning errors.
When to Use It
- When a database operation can fail (not found, validation, or server errors) and you want typed, predictable error handling.
- When server actions can error and you need standardized ActionResponse/RedirectAction patterns.
- When you require user-facing feedback for errors via toast notifications without exposing internal details.
- When you must log rich context (errorCode, isRetryable, userId) and avoid leaking internal stack traces.
- When building resilient UI with error boundaries and fallback UI plus retry mechanisms.
Quick Start
- Step 1: Implement DbError and categorizeDbError, and adopt the [null, Data] | [DbError, null] tuple pattern for DB calls.
- Step 2: In server actions, use ActionResponse/RedirectAction, apply Zod fieldErrors, and set toast cookies for redirect feedback.
- Step 3: On the client, add React error boundaries and toast notifications, and enable structured logging with Pino, including error context.
Best Practices
- Use typed errors (DbError, ActionResponse) to drive deterministic handling across layers.
- Return errors using a consistent [error, data] tuple pattern to simplify caller code.
- Centralize error categorization with categorizeDbError() and log details before returning.
- Show friendly messages via toast notifications instead of exposing internal error data.
- Wrap UI components with React error boundaries and provide clear fallback UI and retry options.
Example Use Cases
- How to handle database errors
- Error boundary patterns
- Toast notifications for errors
- Server action error handling
- Structured logging with context (errorCode, isRetryable, userId)
Frequently Asked Questions
Related Skills
audit
chaterm/terminal-skills
--- name: audit description: 安全审计 version: 1.0.0 author: terminal-skills tags: [security, audit, auditd, logging, compliance, vulnerability] --- # 安全审计 ## 概述 安全审计、漏洞扫描、合规检查技能。 ## auditd 审计系统 ### 安装与管理 ```bash # 安装 apt install auditd audispd-plugins # Debian/Ubuntu yum install audit
structured-logging
JanSzewczyk/claude-plugins
Structured logging patterns with Pino for Next.js applications. Covers log levels, context enrichment, child loggers, and production best practices.
firebase-firestore
JanSzewczyk/claude-plugins
Create Firebase Firestore database queries with TypeScript, proper type lifecycle, error handling, and best practices for Next.js applications.