Communitygithub.com

Gestion des erreurs

Modèles pour une gestion d'erreur robuste en TypeScript, Python et Go. Couvre les erreurs typées, les limites d'erreur, les tentatives, les interrupteurs de circuit et les messages d'erreur utilisateur. À utiliser lors de la conception de types d'erreur, de tentatives, d'interrupteurs de circuit ou de messages d'échec destinés à l'utilisateur en TypeScript, Python ou Go.

Qu'est-ce que Gestion des erreurs ?

Ce skill fournit des modèles complets et cohérents pour une gestion d'erreur robuste dans les applications de production, avec des exemples concrets en TypeScript/JavaScript, Python et Go. Il est conçu pour les développeurs qui doivent structurer leurs erreurs de manière prévisible, avec des classes d'erreur typées (comme `NotFoundError`, `ValidationError`), un modèle de résultat pour les opérations risquées, et des gestionnaires d'erreur standard pour des frameworks comme Next.js, Express et FastAPI. Le skill met l'accent sur cinq principes fondamentaux : échouer rapidement et bruyamment, utiliser des erreurs typées plutôt que des messages 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 API. Il inclut également des implémentations pour la logique de tentative avec backoff exponentiel, les limites d'erreur React, les erreurs sentinelles et l'encapsulation d'erreurs en Go, ainsi qu'une checklist de validation pour garantir une gestion d'erreur complète avant le merge du code. Les exemples de code sont directement exploitables et couvrent des scénarios réels comme les appels à une base de données, les endpoints API et les composants frontend.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/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

Modèles de Gestion d'Erreur

Modèles de gestion d'erreur cohérents et robustes pour les applications de production.

Quand Activer

  • Conception de hiérarchies d'erreurs ou d'exceptions pour un nouveau module ou service
  • Ajout de logique de reprise ou d'interrupteurs de circuit pour des dépendances externes non fiables
  • Révision de points de terminaison API pour détecter des erreurs de gestion manquantes
  • Implémentation de messages d'erreur et de retours utilisateur
  • Débogage de défaillances en cascade ou d'erreurs silencieuses avalées

Principes Fondamentaux

  1. Échouer vite et bruyamment — faire remonter les erreurs à la frontière où elles se produisent ; ne pas les enterrer
  2. Erreurs typées plutôt que messages chaînes — les erreurs sont des valeurs de première classe avec une structure
  3. Messages utilisateur ≠ messages développeur — afficher du texte convivial aux utilisateurs, enregistrer le contexte complet côté serveur
  4. Ne jamais avaler les erreurs silencieusement — chaque bloc catch doit soit traiter, relancer ou enregistrer
  5. Les erreurs font partie de votre contrat 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 une chaîne de prototype correcte dans le JavaScript ES5 transpilé.
    // Requis pour que les vérifications `instanceof` (par 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 débit dépassée', 'RATE_LIMITED', 429)
  }
}

Modèle de Résultat (style sans levée d'exception)

Pour les opérations où l'échec est attendu et courant (analyse syntaxique, 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 base de données', 'DB_ERROR'))
  }
}

const result = await fetchUser('abc-123')
if (!result.ok) {
  // TypeScript connaît ici result.error
  logger.error('Échec de récupération de l'utilisateur', { error: result.error })
  return
}
// TypeScript connaît ici result.value
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: 'Échec de la validation de la requête',
          details: error.issues.map(i => ({
            field: i.path.join('.'),
            message: i.message,
          })),
        },
      },
      { status: 422 },
    )
  }

  // Erreur inattendue — enregistrer les détails, renvoyer 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 actualiser.</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 tous les détails, renvoyer 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 sentinelles pour la vérification de type
var (
    ErrNotFound    = errors.New("not found")
    ErrUnauthorized = errors.New("unauthorized")
    ErrConflict     = errors.New("conflict")
)

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

Tenter 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 : tenter 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 à l'Utilisateur

Mapper les codes d'erreur en messages lisibles par l'homme. 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: 'Un problème est survenu 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 d'Erreur

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

  • Chaque bloc catch traite, relance ou enregistre — pas d'erreur avalée silencieusement
  • Les erreurs API suivent l'enveloppe standard { error: { code, message } }
  • Les messages destinés à l'utilisateur ne contiennent ni traces de pile ni détails internes
  • Le contexte complet de l'erreur est enregistré côté serveur
  • Les classes d'erreur personnalisées étendent une classe de base AppError avec un champ code
  • Les fonctions asynchrones font remonter les erreurs aux appelants — pas de "fire-and-forget" sans solution de repli
  • La logique de reprise ne tente que les erreurs récupérables (pas les erreurs client 4xx)
  • Les composants React sont encapsulé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/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

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.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

Skills associés