Communitygithub.com

gestion-erreurs

Patrons pour une gestion d'erreurs robuste en TypeScript, Python et Go. Couvre les erreurs typées, les limites d'erreur, les tentatives, les disjoncteurs et les messages d'erreur destinés aux utilisateurs. À utiliser lors de la conception de types d'erreur, de tentatives, de disjoncteurs ou de messages d'échec destinés aux utilisateurs en TypeScript, Python ou Go.

Qu'est-ce que gestion-erreurs ?

Cette compétence fournit des patrons de gestion d'erreurs cohérents et robustes pour les applications de production. Elle est conçue pour les développeurs travaillant avec TypeScript, Python et Go, et offre des solutions pour les erreurs typées, les limites d'erreur, les tentatives avec backoff exponentiel, les disjoncteurs et les messages d'erreur destinés aux utilisateurs. La compétence est utile lors de la conception de hiérarchies d'exception pour de nouveaux modules, de l'ajout de logique de tentative pour des dépendances externes, de la validation des points de terminaison API, ou du débogage de défaillances en cascade. Elle supporte les plateformes modernes comme Next.js, Express, FastAPI, et React, et fournit des exemples concrets de code, notamment des classes d'erreur typées, un patron Result, des gestionnaires d'erreur API, un composant ErrorBoundary, et des sentinelles en Go. La compétence met l'accent sur cinq principes clés : échouer rapidement et bruyamment, utiliser des erreurs typées plutôt que des chaînes, séparer les messages utilisateur des messages développeur, ne jamais avaler les erreurs silencieusement, et considérer les erreurs comme faisant partie du contrat de l'API. Elle inclut également une checklist de validation pour s'assurer que le code de gestion d'erreurs est complet avant la fusion.

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

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Patrons de Gestion des Erreurs

Patrons cohérents et robustes pour la gestion des erreurs dans les applications de production.

Quand Activer

  • Concevoir des hiérarchies d'exceptions ou de types d'erreur pour un nouveau module ou service
  • Ajouter une logique de tentative ou des disjoncteurs pour des dépendances externes peu fiables
  • Réviser les points de terminaison API pour détecter une gestion d'erreurs manquante
  • Implémenter des messages d'erreur et des retours utilisateur
  • Déboguer des défaillances en cascade ou l'absorption silencieuse d'erreurs

Principes Fondamentaux

  1. Échouer rapidement et bruyamment — signaler les erreurs à la frontière où elles surviennent ; ne pas les enterrer
  2. Erreurs typées plutôt que messages chaîne — les erreurs sont des valeurs de premier ordre avec une structure
  3. Messages utilisateur ≠ messages développeur — afficher un texte convivial aux utilisateurs, enregistrer le contexte complet côté serveur
  4. Ne jamais avaler les erreurs silencieusement — chaque bloc catch doit soit gérer, soit relancer, soit enregistrer
  5. Les erreurs font partie de votre contrat d'API — documenter chaque code d'erreur qu'un client peut recevoir

TypeScript / JavaScript

Classes d'Erreur Typées

// Définir une hiérarchie d'erreurs pour votre domaine
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
    // Maintenir la chaîne de prototype correcte dans le JavaScript ES5 transpilé.
    // Nécessaire pour que les vérifications `instanceof` (ex : `error instanceof NotFoundError`)
    // fonctionnent correctement lors de l'extension de la classe Error intégrée.
    Object.setPrototypeOf(this, new.target.prototype)
  }
}

export class NotFoundError extends AppError {
  constructor(resource: string, id: string) {
    super(`${resource} introuvable : ${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 = 'Authentification requise') {
    super(reason, 'UNAUTHORIZED', 401)
  }
}

export class RateLimitError extends AppError {
  constructor(public readonly retryAfterMs: number) {
    super('Limite de taux dépassée', 'RATE_LIMITED', 429)
  }
}

Patron Result (style sans exception)

Pour les opérations où l'échec est prévisible et courant (analyse, appels externes) :

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 }
}

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

const result = await fetchUser('abc-123')
if (!result.ok) {
  // TypeScript sait que result.error est ici
  logger.error('Échec de la récupération de l\'utilisateur', { error: result.error })
  return
}
// TypeScript sait que result.value est ici
console.log(result.value.email)

Gestionnaire d'Erreur API (Next.js / Express)

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

function handleApiError(error: unknown): NextResponse {
  // Erreur d'application connue
  if (error instanceof AppError) {
    return NextResponse.json(
      {
        error: {
          code: error.code,
          message: error.message,
          ...(error.details ? { details: error.details } : {}),
        },
      },
      { status: error.statusCode },
    )
  }

  // Erreur de validation Zod
  if (error instanceof z.ZodError) {
    return NextResponse.json(
      {
        error: {
          code: 'VALIDATION_ERROR',
          message: 'La validation de la requête a échoué',
          details: error.issues.map(i => ({
            field: i.path.join('.'),
            message: i.message,
          })),
        },
      },
      { status: 422 },
    )
  }

  // Erreur inattendue — enregistrer les détails, retourner un message générique
  console.error('Erreur inattendue :', error)
  return NextResponse.json(
    { error: { code: 'INTERNAL_ERROR', message: 'Une erreur inattendue s\'est produite' } },
    { status: 500 },
  )
}

export async function POST(req: NextRequest) {
  try {
    // ... logique du gestionnaire
  } catch (error) {
    return handleApiError(error)
  }
}

Limite d'Erreur 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('Erreur React non gérée :', error, info)
  }

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

// Utilisation
<ErrorBoundary fallback={<p>Quelque chose s\'est mal passé. Veuillez rafraîchir.</p>}>
  <MyComponent />
</ErrorBoundary>

Python

Hiérarchie d'Exceptions Personnalisées

class AppError(Exception):
    """Erreur d'application de base."""
    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} introuvable : {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 []

Gestionnaire d'Exception Global 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:
    # Enregistrer les détails complets, retourner un message générique
    logger.exception("Erreur inattendue", exc_info=exc)
    return JSONResponse(
        status_code=500,
        content={"error": {"code": "INTERNAL_ERROR", "message": "Une erreur inattendue s\'est produite"}},
    )

Go

Erreurs Sentinel et Encapsulation d'Erreurs

package domain

import "errors"

// Erreurs sentinel pour la vérification de type
var (
    ErrNotFound    = errors.New("introuvable")
    ErrUnauthorized = errors.New("non autorisé")
    ErrConflict     = errors.New("conflit")
)

// Encapsuler les erreurs avec contexte — ne jamais perdre l'originale
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("utilisateur %s : %w", id, ErrNotFound)
    }
    if err != nil {
        return nil, fmt.Errorf("requête pour l'utilisateur %s : %w", id, err)
    }
    return user, nil
}

// Au niveau du gestionnaire, déballer pour déterminer la réponse
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", "Accès refusé")
        default:
            slog.Error("erreur inattendue", "err", err)
            writeError(w, http.StatusInternalServerError, "internal_error", "Une erreur inattendue s\'est produite")
        }
        return
    }
    writeJSON(w, http.StatusOK, user)
}

Tentative avec Backoff Exponentiel

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
}

// Utilisation : réessayer les erreurs réseau transitoires, pas les 4xx
const data = await withRetry(() => fetch('/api/data').then(r => r.json()), {
  maxAttempts: 3,
  retryIf: (error) => !(error instanceof AppError && error.statusCode < 500),
})

Messages d'Erreur Destinés aux Utilisateurs

Mapper les codes d'erreur en messages lisibles par l'humain. Garder les détails techniques hors du texte visible par l'utilisateur.

const USER_ERROR_MESSAGES: Record<string, string> = {
  NOT_FOUND: 'L\'élément demandé est introuvable.',
  UNAUTHORIZED: 'Veuillez vous connecter pour continuer.',
  FORBIDDEN: 'Vous n\'avez pas la permission de faire cela.',
  VALIDATION_ERROR: 'Veuillez vérifier votre saisie et réessayer.',
  RATE_LIMITED: 'Trop de requêtes. Veuillez patienter un moment et réessayer.',
  INTERNAL_ERROR: 'Quelque chose s\'est mal passé de notre côté. Veuillez réessayer plus tard.',
}

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

Checklist de Gestion des Erreurs

Avant de fusionner tout code qui touche à la gestion des erreurs :

  • Chaque bloc catch gère, relance ou enregistre — pas d'absorption silencieuse
  • Les erreurs API suivent le format standard { error: { code, message } }
  • Les messages destinés aux utilisateurs ne contiennent pas de traces de pile ni de détails internes
  • Le contexte complet de l'erreur est enregistré côté serveur
  • Les classes d'erreur personnalisées étendent une classe AppError de base avec un champ code
  • Les fonctions asynchrones signalent les erreurs aux appelants — pas de "fire-and-forget" sans solution de repli
  • La logique de tentative ne réessaie que les erreurs réessayables (pas les erreurs client 4xx)
  • Les composants React sont enveloppés dans ErrorBoundary pour les erreurs de rendu

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 associés