Communitygithub.com

patrones-de-manejo-de-errores

Patrones para un manejo de errores robusto en TypeScript, Python y Go. Cubre errores tipados, límites de error, reintentos, interruptores de circuito y mensajes de error orientados al usuario. Úselo al diseñar tipos de error, reintentos, interruptores de circuito o mensajes de falla orientados al usuario en TypeScript, Python o Go.

¿Qué es patrones-de-manejo-de-errores?

Este skill proporciona una guía completa y práctica para implementar un manejo de errores consistente y robusto en aplicaciones de producción, soportando los lenguajes TypeScript, Python y Go. Se enfoca en principios clave como fallar rápida y ruidosamente, usar errores tipados en lugar de mensajes de texto, separar los mensajes para el usuario de los detalles para el desarrollador, y nunca tragar errores en silencio. Incluye patrones específicos para cada lenguaje: clases de error tipadas y un patrón Result (similar a un tipo Either) en TypeScript, jerarquías de excepciones personalizadas en Python, y errores centinela con envoltura de errores en Go. Además, aborda técnicas avanzadas como reintentos con retroceso exponencial, límites de error en React, y un checklist de verificación para asegurar que todo manejo de errores cumpla con las mejores prácticas antes de fusionar código.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/error-handling

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Patrones de Manejo de Errores

Patrones de manejo de errores consistentes y robustos para aplicaciones de producción.

Cuándo Activar

  • Al diseñar tipos de error o jerarquías de excepciones para un nuevo módulo o servicio
  • Al agregar lógica de reintentos o interruptores de circuito para dependencias externas no confiables
  • Al revisar puntos finales de API para detectar manejo de errores faltante
  • Al implementar mensajes de error y retroalimentación orientados al usuario
  • Al depurar fallos en cascada o la eliminación silenciosa de errores

Principios Fundamentales

  1. Fallar rápida y ruidosamente — muestre los errores en el límite donde ocurren; no los entierre
  2. Errores tipados sobre mensajes de texto — los errores son valores de primera clase con estructura
  3. Mensajes de usuario ≠ mensajes de desarrollador — muestre texto amigable a los usuarios, registre el contexto completo en el servidor
  4. Nunca trague errores en silencio — cada bloque catch debe manejar, relanzar o registrar el error
  5. Los errores son parte de su contrato de API — documente cada código de error que un cliente pueda recibir

TypeScript / JavaScript

Clases de Error Tipadas

// Defina una jerarquía de errores para su dominio
export class AppError extends Error {
  constructor(
    message: string,
    public readonly code: string,
    public readonly statusCode: number = 500,
    public readonly details?: unknown,
  ) {
    super(message)
    this.name = this.constructor.name
    // Mantiene la cadena de prototipos correcta en JavaScript ES5 transpilado.
    // Requerido para que las comprobaciones `instanceof` (por ejemplo, `error instanceof NotFoundError`)
    // funcionen correctamente al extender la clase Error incorporada.
    Object.setPrototypeOf(this, new.target.prototype)
  }
}

export class NotFoundError extends AppError {
  constructor(resource: string, id: string) {
    super(`${resource} no encontrado: ${id}`, 'NOT_FOUND', 404)
  }
}

export class ValidationError extends AppError {
  constructor(message: string, details: { field: string; message: string }[]) {
    super(message, 'VALIDATION_ERROR', 422, details)
  }
}

export class UnauthorizedError extends AppError {
  constructor(reason = 'Autenticación requerida') {
    super(reason, 'UNAUTHORIZED', 401)
  }
}

export class RateLimitError extends AppError {
  constructor(public readonly retryAfterMs: number) {
    super('Límite de velocidad excedido', 'RATE_LIMITED', 429)
  }
}

Patrón Result (estilo sin lanzamiento)

Para operaciones donde el fallo es esperado y común (análisis, llamadas externas):

type Result<T, E = AppError> =
  | { ok: true; value: T }
  | { ok: false; error: E }

function ok<T>(value: T): Result<T> {
  return { ok: true, value }
}

function err<E>(error: E): Result<never, E> {
  return { ok: false, error }
}

// Uso
async function fetchUser(id: string): Promise<Result<User>> {
  try {
    const user = await db.users.findUnique({ where: { id } })
    if (!user) return err(new NotFoundError('Usuario', id))
    return ok(user)
  } catch (e) {
    return err(new AppError('Error de base de datos', 'DB_ERROR'))
  }
}

const result = await fetchUser('abc-123')
if (!result.ok) {
  // TypeScript sabe que result.error está aquí
  logger.error('Error al obtener usuario', { error: result.error })
  return
}
// TypeScript sabe que result.value está aquí
console.log(result.value.email)

Manejador de Error de API (Next.js / Express)

import { NextRequest, NextResponse } from 'next/server'

function handleApiError(error: unknown): NextResponse {
  // Error de aplicación conocido
  if (error instanceof AppError) {
    return NextResponse.json(
      {
        error: {
          code: error.code,
          message: error.message,
          ...(error.details ? { details: error.details } : {}),
        },
      },
      { status: error.statusCode },
    )
  }

  // Error de validación Zod
  if (error instanceof z.ZodError) {
    return NextResponse.json(
      {
        error: {
          code: 'VALIDATION_ERROR',
          message: 'La validación de la solicitud falló',
          details: error.issues.map(i => ({
            field: i.path.join('.'),
            message: i.message,
          })),
        },
      },
      { status: 422 },
    )
  }

  // Error inesperado — registre detalles, devuelva mensaje genérico
  console.error('Error inesperado:', error)
  return NextResponse.json(
    { error: { code: 'INTERNAL_ERROR', message: 'Ocurrió un error inesperado' } },
    { status: 500 },
  )
}

export async function POST(req: NextRequest) {
  try {
    // ... lógica del manejador
  } catch (error) {
    return handleApiError(error)
  }
}

Límite de Error de React

import { Component, ErrorInfo, ReactNode } from 'react'

interface Props {
  fallback: ReactNode
  onError?: (error: Error, info: ErrorInfo) => void
  children: ReactNode
}

interface State {
  hasError: boolean
  error: Error | null
}

export class ErrorBoundary extends Component<Props, State> {
  state: State = { hasError: false, error: null }

  static getDerivedStateFromError(error: Error): State {
    return { hasError: true, error }
  }

  componentDidCatch(error: Error, info: ErrorInfo) {
    this.props.onError?.(error, info)
    console.error('Error de React no manejado:', error, info)
  }

  render() {
    if (this.state.hasError) return this.props.fallback
    return this.props.children
  }
}

// Uso
<ErrorBoundary fallback={<p>Algo salió mal. Por favor, recargue.</p>}>
  <MyComponent />
</ErrorBoundary>

Python

Jerarquía de Excepciones Personalizada

class AppError(Exception):
    """Error base de la aplicación."""
    def __init__(self, message: str, code: str, status_code: int = 500):
        super().__init__(message)
        self.code = code
        self.status_code = status_code

class NotFoundError(AppError):
    def __init__(self, resource: str, id: str):
        super().__init__(f"{resource} no encontrada: {id}", "NOT_FOUND", 404)

class ValidationError(AppError):
    def __init__(self, message: str, details: list[dict] | None = None):
        super().__init__(message, "VALIDATION_ERROR", 422)
        self.details = details or []

Manejador Global de Excepciones de FastAPI

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI()

@app.exception_handler(AppError)
async def app_error_handler(request: Request, exc: AppError) -> JSONResponse:
    return JSONResponse(
        status_code=exc.status_code,
        content={"error": {"code": exc.code, "message": str(exc)}},
    )

@app.exception_handler(Exception)
async def generic_error_handler(request: Request, exc: Exception) -> JSONResponse:
    # Registre detalles completos, devuelva mensaje genérico
    logger.exception("Error inesperado", exc_info=exc)
    return JSONResponse(
        status_code=500,
        content={"error": {"code": "INTERNAL_ERROR", "message": "Ocurrió un error inesperado"}},
    )

Go

Errores Centinela y Envoltura de Errores

package domain

import "errors"

// Errores centinela para verificación de tipos
var (
    ErrNotFound    = errors.New("no encontrado")
    ErrUnauthorized = errors.New("no autorizado")
    ErrConflict     = errors.New("conflicto")
)

// Envuelva errores con contexto — nunca pierda el original
func (r *UserRepository) FindByID(ctx context.Context, id string) (*User, error) {
    user, err := r.db.QueryRow(ctx, "SELECT * FROM users WHERE id = $1", id)
    if errors.Is(err, sql.ErrNoRows) {
        return nil, fmt.Errorf("usuario %s: %w", id, ErrNotFound)
    }
    if err != nil {
        return nil, fmt.Errorf("consultando usuario %s: %w", id, err)
    }
    return user, nil
}

// En el nivel del manejador, desenvuelva para determinar la respuesta
func (h *Handler) GetUser(w http.ResponseWriter, r *http.Request) {
    user, err := h.service.GetUser(r.Context(), chi.URLParam(r, "id"))
    if err != nil {
        switch {
        case errors.Is(err, domain.ErrNotFound):
            writeError(w, http.StatusNotFound, "not_found", err.Error())
        case errors.Is(err, domain.ErrUnauthorized):
            writeError(w, http.StatusForbidden, "forbidden", "Acceso denegado")
        default:
            slog.Error("error inesperado", "err", err)
            writeError(w, http.StatusInternalServerError, "internal_error", "Ocurrió un error inesperado")
        }
        return
    }
    writeJSON(w, http.StatusOK, user)
}

Reintento con Retroceso Exponencial

interface RetryOptions {
  maxAttempts?: number
  baseDelayMs?: number
  maxDelayMs?: number
  retryIf?: (error: unknown) => boolean
}

async function withRetry<T>(
  fn: () => Promise<T>,
  options: RetryOptions = {},
): Promise<T> {
  const {
    maxAttempts = 3,
    baseDelayMs = 500,
    maxDelayMs = 10_000,
    retryIf = () => true,
  } = options

  let lastError: unknown

  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await fn()
    } catch (error) {
      lastError = error
      if (attempt === maxAttempts || !retryIf(error)) throw error

      const jitter = Math.random() * baseDelayMs
      const delay = Math.min(baseDelayMs * 2 ** (attempt - 1) + jitter, maxDelayMs)
      await new Promise(resolve => setTimeout(resolve, delay))
    }
  }

  throw lastError
}

// Uso: reintentar errores de red transitorios, no 4xx
const data = await withRetry(() => fetch('/api/data').then(r => r.json()), {
  maxAttempts: 3,
  retryIf: (error) => !(error instanceof AppError && error.statusCode < 500),
})

Mensajes de Error Orientados al Usuario

Mapee códigos de error a mensajes legibles por humanos. Mantenga los detalles técnicos fuera del texto visible para el usuario.

const USER_ERROR_MESSAGES: Record<string, string> = {
  NOT_FOUND: 'El elemento solicitado no pudo ser encontrado.',
  UNAUTHORIZED: 'Inicie sesión para continuar.',
  FORBIDDEN: 'No tiene permiso para hacer eso.',
  VALIDATION_ERROR: 'Verifique su entrada e intente de nuevo.',
  RATE_LIMITED: 'Demasiadas solicitudes. Espere un momento e intente de nuevo.',
  INTERNAL_ERROR: 'Algo salió mal de nuestro lado. Intente de nuevo más tarde.',
}

export function getUserMessage(code: string): string {
  return USER_ERROR_MESSAGES[code] ?? USER_ERROR_MESSAGES.INTERNAL_ERROR
}

Lista de Verificación de Manejo de Errores

Antes de fusionar cualquier código que toque el manejo de errores:

  • Cada bloque catch maneja, relanza o registra — sin eliminación silenciosa
  • Los errores de API siguen el formato estándar { error: { code, message } }
  • Los mensajes orientados al usuario no contienen trazas de pila ni detalles internos
  • El contexto completo del error se registra en el servidor
  • Las clases de error personalizadas extienden una base AppError con un campo code
  • Las funciones asíncronas muestran errores a los llamantes — sin "disparar y olvidar" sin respaldo
  • La lógica de reintento solo reintenta errores reintentables (no errores de cliente 4xx)
  • Los componentes de React están envueltos en ErrorBoundary para errores de renderizado

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Skills relacionados