— No reviews yet
0 installs
13 views
0.0% view→install
Install
$ agentstack add skill-kensaurus-cursor-kenji-backend-error-handling ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Are you the author of Backend Error Handling? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claimAbout
Error Handling Skill
full error handling patterns for full-stack applications.
When to Use
- Adding error handling to new features
- Improving error user experience
- Standardizing error responses
- Debugging error propagation
- Adding error monitoring
CRITICAL: Check Existing First
Before adding ANY error handling, verify:
- Check for existing error types:
rg "type.*Error|interface.*Error" --type ts
rg "ActionResult|ApiError" --type ts
- Check for existing error boundaries:
ls -la app/error.tsx app/global-error.tsx
rg "ErrorBoundary" --type tsx
- Check for existing error utilities:
rg "formatError|handleError|reportError" --type ts
ls -la src/lib/errors* src/lib/error* 2>/dev/null # @/lib/errors
- Check established error response patterns:
rg "success: false|error:" src/features/*/server/ --type ts | head -10
Why: Inconsistent error handling confuses users and complicates debugging. Always follow established patterns.
Error Handling Layers
┌─────────────────────────────────────────┐
│ UI Layer │
│ - Error boundaries │
│ - Form validation errors │
│ - Toast notifications │
├─────────────────────────────────────────┤
│ Application Layer │
│ - Server Action errors │
│ - API route errors │
│ - Business logic errors │
├─────────────────────────────────────────┤
│ Data Layer │
│ - Database errors │
│ - Validation errors (Zod) │
│ - External API errors │
└─────────────────────────────────────────┘
Standard Error Types
// types/errors.ts
// Base error shape
interface AppError {
code: string // Machine-readable: VALIDATION_ERROR
message: string // User-friendly message
details?: unknown // Additional context
}
// Action result pattern
type ActionResult =
| { success: true; data: T }
| { success: false; error: AppError }
// Common error codes
const ErrorCode = {
VALIDATION_ERROR: 'VALIDATION_ERROR',
NOT_FOUND: 'NOT_FOUND',
UNAUTHORIZED: 'UNAUTHORIZED',
FORBIDDEN: 'FORBIDDEN',
CONFLICT: 'CONFLICT',
RATE_LIMITED: 'RATE_LIMITED',
INTERNAL_ERROR: 'INTERNAL_ERROR',
} as const
Server Action Error Handling
// features/users/server/actions.ts
'use server'
import { z } from 'zod'
import { revalidatePath } from 'next/cache'
const CreateUserSchema = z.object({
email: z.string().email('Invalid email address'),
name: z.string().min(1, 'Name is required'),
})
export async function createUser(
prevState: ActionResult,
formData: FormData
): Promise> {
try {
// 1. Validate input
const validated = CreateUserSchema.safeParse({
email: formData.get('email'),
name: formData.get('name'),
})
if (!validated.success) {
return {
success: false,
error: {
code: 'VALIDATION_ERROR',
message: 'Invalid input',
details: validated.error.flatten().fieldErrors,
},
}
}
// 2. Check authorization
const session = await auth()
if (!session) {
return {
success: false,
error: {
code: 'UNAUTHORIZED',
message: 'Please sign in to continue',
},
}
}
// 3. Execute business logic
const user = await db.user.create({
data: validated.data,
})
revalidatePath('/users')
return { success: true, data: user }
} catch (error) {
// 4. Handle known errors
if (error instanceof Prisma.PrismaClientKnownRequestError) {
if (error.code === 'P2002') {
return {
success: false,
error: {
code: 'CONFLICT',
message: 'A user with this email already exists',
},
}
}
}
// 5. Log unknown errors, return generic message
console.error('createUser error:', error)
return {
success: false,
error: {
code: 'INTERNAL_ERROR',
message: 'Something went wrong. Please try again.',
},
}
}
}
Form Error Display (React 19+)
// components/UserForm.tsx
'use client'
import { useActionState } from 'react'
import { useFormStatus } from 'react-dom'
import { createUser } from '@/features/users/server/actions'
// Separate submit button to use useFormStatus
function SubmitButton() {
const { pending } = useFormStatus()
return (
{pending ? 'Creating...' : 'Create User'}
)
}
export function UserForm() {
const [state, action, isPending] = useActionState(createUser, null)
// Get field errors from validation
const fieldErrors = state?.success === false
? state.error.details as Record
: {}
return (
{/* Global error */}
{state?.success === false && state.error.code !== 'VALIDATION_ERROR' && (
{state.error.message}
)}
{/* Field with error */}
Email
{fieldErrors.email && (
{fieldErrors.email[0]}
)}
)
}
React 19 Form Patterns:
useActionState- Form state with Server ActionsuseFormStatus- Pending state in child componentsuseOptimistic- Optimistic UI updates
## React Error Boundaries
```tsx
// app/error.tsx (Next.js page error boundary)
'use client'
import { useEffect } from 'react'
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
useEffect(() => {
// Log to error reporting service
console.error('Page error:', error)
}, [error])
return (
Something went wrong
We're sorry, but something unexpected happened.
Try again
)
}
// app/global-error.tsx (root error boundary)
'use client'
export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
Something went wrong!
Try again
)
}
API Route Error Handling
// app/api/products/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const validated = ProductSchema.safeParse(body)
if (!validated.success) {
return NextResponse.json(
{
error: {
code: 'VALIDATION_ERROR',
message: 'Invalid request body',
details: validated.error.flatten().fieldErrors,
},
},
{ status: 400 }
)
}
const product = await db.product.create({ data: validated.data })
return NextResponse.json({ data: product }, { status: 201 })
} catch (error) {
if (error instanceof SyntaxError) {
return NextResponse.json(
{ error: { code: 'INVALID_JSON', message: 'Invalid JSON body' } },
{ status: 400 }
)
}
console.error('POST /api/products error:', error)
return NextResponse.json(
{ error: { code: 'INTERNAL_ERROR', message: 'Internal server error' } },
{ status: 500 }
)
}
}
TanStack Query Error Handling
// hooks/useProducts.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
export function useProducts() {
return useQuery({
queryKey: ['products'],
queryFn: async () => {
const res = await fetch('/api/products')
if (!res.ok) {
const error = await res.json()
throw new Error(error.error?.message || 'Failed to fetch products')
}
return res.json()
},
retry: (failureCount, error) => {
// Don't retry on 4xx errors
if (error.message.includes('401') || error.message.includes('403')) {
return false
}
return failureCount {
const res = await fetch('/api/products', {
method: 'POST',
body: JSON.stringify(data),
})
if (!res.ok) {
const error = await res.json()
throw new Error(error.error?.message || 'Failed to create product')
}
return res.json()
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['products'] })
toast.success('Product created')
},
onError: (error) => {
toast.error(error.message)
},
})
}
Error State UI Components
// components/ErrorState.tsx
import { AlertCircle, RefreshCw } from 'lucide-react'
interface ErrorStateProps {
title?: string
message: string
onRetry?: () => void
}
export function ErrorState({
title = 'Error',
message,
onRetry
}: ErrorStateProps) {
return (
{title}
{message}
{onRetry && (
Try again
)}
)
}
// Usage with TanStack Query
function ProductList() {
const { data, error, isLoading, refetch } = useProducts()
if (error) {
return (
refetch()}
/>
)
}
// ...
}
Further reading
- [Error Logging & Monitoring and more](references/details.md)
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: kensaurus
- Source: kensaurus/cursor-kenji
- License: MIT
- Homepage: https://github.com/kensaurus/cursor-kenji
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.