Error Handling Skill Expert structured error handling for FastAPI backends and React/Next.js frontends with consistent error messages and logging. Quick Reference | Pattern | Backend | Frontend | |---------|---------|----------| | Custom exception | | N/A | | Try-catch | | | | Global handler | | component | | User message | field in response | Toast/Snackbar | | Error logging | | Console/Sentry | Custom Exceptions (Backend) Base Exception Hierarchy Domain-Specific Exceptions Try-Catch Patterns Narrow Try Blocks Cleanup with Finally Error Logging (Backend) Structured JSON Logging Using the Log…

, frontmatter, re.MULTILINE)\n if not name_match:\n print(\"✗ Missing 'name' in frontmatter\")\n sys.exit(1)\n name = name_match.group(1).strip()\n\n # Extract description (handles multi-line YAML with | or >)\n desc_match = re.search(r'^description:\\s*[\\|>]?\\s*\\n?(.*)', frontmatter, re.MULTILINE | re.DOTALL)\n if not desc_match:\n print(\"✗ Missing 'description' in frontmatter\")\n sys.exit(1)\n desc = desc_match.group(1).strip()\n\n # Check gerund naming\n if not re.match(r'^[a-z][a-z0-9-]*

Error Handling Skill Expert structured error handling for FastAPI backends and React/Next.js frontends with consistent error messages and logging. Quick Reference | Pattern | Backend | Frontend | |---------|---------|----------| | Custom exception | | N/A | | Try-catch | | | | Global handler | | component | | User message | field in response | Toast/Snackbar | | Error logging | | Console/Sentry | Custom Exceptions (Backend) Base Exception Hierarchy Domain-Specific Exceptions Try-Catch Patterns Narrow Try Blocks Cleanup with Finally Error Logging (Backend) Structured JSON Logging Using the Log…

, name):\n print(f\"✗ Invalid name format: {name}\")\n sys.exit(1)\n\n # Check for \"Use when\" trigger\n if 'use when' not in desc.lower():\n print(\"✗ Description missing 'Use when' trigger\")\n sys.exit(1)\n\n # Check verify.py exists\n verify_path = os.path.join(skill_path, \"scripts\", \"verify.py\")\n if not os.path.isfile(verify_path):\n print(\"✗ scripts/verify.py missing\")\n sys.exit(1)\n\n print(f\"✓ {name} valid\")\n sys.exit(0)\n\nif __name__ == \"__main__\":\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":1902,"content_sha256":"2506808ccdd732a3b016fbdb5bb7d919d31c867996cd7b28beb5438bf2329ea2"},{"filename":"skill-report.json","content":"{\n \"schema_version\": \"2.0\",\n \"meta\": {\n \"generated_at\": \"2026-01-21T16:49:31.900Z\",\n \"slug\": \"awais68-error-handling\",\n \"source_url\": \"https://github.com/Awais68/hackathon-2-phase-ii-full-stack-web-app/tree/main/.claude/skills/error-handling\",\n \"source_ref\": \"main\",\n \"model\": \"claude\",\n \"analysis_version\": \"3.0.0\",\n \"source_type\": \"community\",\n \"content_hash\": \"1a343dcf17788cf81526485beb5e3bf580cbe69a086889936063b538dcd3eac9\",\n \"tree_hash\": \"3db126649188939c11d4c6c62e5c5d38deae6adda87e7d74d8e42ec2647a5537\"\n },\n \"skill\": {\n \"name\": \"error-handling\",\n \"description\": \"Use when implementing structured error handling in backend or frontend code.\\nTriggers for: try-catch patterns, custom exception classes, global error handlers,\\nerror logging, user-friendly error messages, or API error responses.\\nNOT for: business logic validation (use domain exceptions) or unrelated error types.\\n\",\n \"summary\": \"Implement structured error handling patterns for FastAPI backends and React frontends with consistent error messages and logging.\",\n \"icon\": \"🛡️\",\n \"version\": \"1.0.0\",\n \"author\": \"Awais68\",\n \"license\": \"MIT\",\n \"category\": \"coding\",\n \"tags\": [\n \"error-handling\",\n \"exceptions\",\n \"fastapi\",\n \"react\",\n \"logging\"\n ],\n \"supported_tools\": [\n \"claude\",\n \"codex\",\n \"claude-code\"\n ],\n \"risk_factors\": [\n \"scripts\",\n \"external_commands\"\n ]\n },\n \"security_audit\": {\n \"risk_level\": \"safe\",\n \"is_blocked\": false,\n \"safe_to_publish\": true,\n \"summary\": \"This skill provides legitimate error handling patterns for FastAPI backends and React frontends. Static findings are false positives triggered by regex flags misidentified as cryptographic algorithms, backticks for markdown code formatting, and error code naming conventions. No malicious patterns confirmed.\",\n \"files_scanned\": 3,\n \"total_lines\": 1398,\n \"audit_model\": \"claude\",\n \"audited_at\": \"2026-01-21T16:49:31.900Z\",\n \"risk_factors\": [\n \"scripts\",\n \"external_commands\"\n ],\n \"risk_factor_evidence\": [\n {\n \"factor\": \"scripts\",\n \"evidence\": [\n {\n \"file\": \"scripts/verify.py\",\n \"line_start\": 1,\n \"line_end\": 62\n }\n ]\n },\n {\n \"factor\": \"external_commands\",\n \"evidence\": [\n {\n \"file\": \"SKILL.md\",\n \"line_start\": 18,\n \"line_end\": 22\n },\n {\n \"file\": \"SKILL.md\",\n \"line_start\": 28,\n \"line_end\": 114\n }\n ]\n }\n ],\n \"critical_findings\": [],\n \"high_findings\": [],\n \"medium_findings\": [],\n \"low_findings\": [],\n \"dangerous_patterns\": []\n },\n \"content\": {\n \"user_title\": \"Implement Structured Error Handling\",\n \"value_statement\": \"Building robust error handling is challenging without consistent patterns. This skill provides ready-to-use exception hierarchies, try-catch patterns, global error handlers, and logging utilities for FastAPI and React applications.\",\n \"seo_keywords\": [\n \"Claude\",\n \"Codex\",\n \"Claude Code\",\n \"error handling\",\n \"exception handling\",\n \"FastAPI errors\",\n \"React error boundary\",\n \"structured logging\",\n \"API error responses\",\n \"try-catch patterns\"\n ],\n \"actual_capabilities\": [\n \"Create custom exception hierarchies extending AppException for backend APIs\",\n \"Implement global error handlers for FastAPI and React ErrorBoundary components\",\n \"Generate user-friendly error messages separate from technical details\",\n \"Set up structured JSON logging with correlation IDs for request tracing\",\n \"Build frontend error mappers to translate API errors to user notifications\",\n \"Configure API clients with proper error response handling\"\n ],\n \"limitations\": [\n \"Does not handle business logic validation errors (use domain exceptions instead)\",\n \"Does not integrate with specific observability platforms like Datadog or New Relic\",\n \"Does not provide retry logic or circuit breaker patterns for external service calls\",\n \"Does not include error monitoring or alerting configuration\"\n ],\n \"use_cases\": [\n {\n \"title\": \"Build Consistent API Error Responses\",\n \"description\": \"Create a unified error response format across all API endpoints with custom exception classes, user-facing messages, and structured logging for easier debugging.\",\n \"target_user\": \"Backend developers building FastAPI applications\"\n },\n {\n \"title\": \"Handle Frontend API Errors Gracefully\",\n \"description\": \"Implement error handling in React applications that displays user-friendly messages, handles network failures, and integrates with toast notifications.\",\n \"target_user\": \"Frontend developers working with API integrations\"\n },\n {\n \"title\": \"Debug Production Issues with Correlation IDs\",\n \"description\": \"Set up structured logging that tracks requests across services using correlation IDs, making it easier to trace errors in distributed systems.\",\n \"target_user\": \"Full-stack developers and DevOps engineers\"\n }\n ],\n \"prompt_templates\": [\n {\n \"title\": \"Create Custom Exception\",\n \"prompt\": \"Create a custom exception class called {ExceptionName} that extends AppException for a FastAPI backend. It should accept {parameters} and return a {status_code} status code with a user-friendly message.\",\n \"scenario\": \"When you need to add a new domain-specific error type\"\n },\n {\n \"title\": \"Add Global Error Handler\",\n \"prompt\": \"Add a global error handler to my FastAPI app that catches AppException and returns a consistent JSON response with error code, user message, and internal details in debug mode.\",\n \"scenario\": \"When setting up error handling middleware\"\n },\n {\n \"title\": \"Handle API Response Errors\",\n \"prompt\": \"Create an error mapper function in TypeScript that translates API error responses into user-friendly messages. It should handle 401, 403, 404, 409, 422, 429, and 500 status codes.\",\n \"scenario\": \"When building React error handling for API calls\"\n },\n {\n \"title\": \"Implement Structured Error Logging\",\n \"prompt\": \"Create a structured logger in Python that outputs JSON logs with timestamp, level, correlation ID, error type, message, and stack trace. Use contextvars for correlation ID tracking.\",\n \"scenario\": \"When setting up application logging\"\n }\n ],\n \"output_examples\": [\n {\n \"input\": \"Create a custom exception for when a resource is not found\",\n \"output\": [\n \"class NotFoundError(AppException):\",\n \" \\\"\\\"\\\"Resource not found (404).\\\"\\\"\\\"\",\n \"\",\n \" def __init__(self, resource: str, identifier: str):\",\n \" super().__init__(\",\n \" status_code=404,\",\n \" detail=f\\\"{resource} with id '{identifier}' not found\\\",\",\n \" user_message=f\\\"{resource} not found. Please check and try again.\\\",\",\n \" )\"\n ]\n },\n {\n \"input\": \"How should I format API error responses?\",\n \"output\": \"{\\n \\\"error\\\": {\\n \\\"code\\\": \\\"STUDENT_NOT_FOUND\\\",\\n \\\"message\\\": \\\"Student not found. Please check and try again.\\\",\\n \\\"internal\\\": \\\"Student with id '12345' not found\\\"\\n }\\n}\"\n },\n {\n \"input\": \"Handle a fetch error in React and show a toast notification\",\n \"output\": \"try {\\n const response = await fetch(url);\\n if (!response.ok) {\\n throw new ApiResponseError(\\n { code: \\\"FETCH_ERROR\\\", message: \\\"Failed to fetch data\\\" },\\n response.status\\n );\\n }\\n return response.json();\\n} catch (error) {\\n if (error instanceof ApiResponseError) {\\n toast.error(error.userMessage);\\n } else {\\n toast.error(\\\"An unexpected error occurred.\\\");\\n }\\n}\"\n }\n ],\n \"best_practices\": [\n \"Separate internal error details from user-facing messages to avoid leaking sensitive information\",\n \"Use specific exception types rather than catching generic Exception to enable targeted error handling\",\n \"Always log exceptions with correlation IDs to trace errors across distributed services\"\n ],\n \"anti_patterns\": [\n \"Catching Exception or bare except clauses that swallow all errors without proper handling\",\n \"Exposing stack traces or internal error details directly to users in production\",\n \"Returning different error response formats across endpoints, making client error handling inconsistent\"\n ],\n \"faq\": [\n {\n \"question\": \"What is the difference between detail and user_message?\",\n \"answer\": \"The detail field contains technical information for debugging (e.g., database errors, variable values). The user_message contains a friendly message safe to display to end users. Only expose detail in debug mode.\"\n },\n {\n \"question\": \"How do I handle validation errors in FastAPI?\",\n \"answer\": \"Use the ValidationError exception class that extends AppException. FastAPI automatically returns 400 for Pydantic validation failures. For custom validation, raise your ValidationError with field name and reason.\"\n },\n {\n \"question\": \"Should I use try-catch or global handlers?\",\n \"answer\": \"Use both. Global handlers catch unhandled exceptions at the top level. Try-catch blocks handle specific errors where they occur, allowing you to add context, retry, or transform the error before it propagates.\"\n },\n {\n \"question\": \"How do I track errors across microservices?\",\n \"answer\": \"Use correlation IDs passed through request headers. Each service logs with the correlation ID from the request. This creates a trace ID that links logs across services for distributed debugging.\"\n },\n {\n \"question\": \"What status codes should I use for different error types?\",\n \"answer\": \"400 for validation errors, 401 for authentication, 403 for authorization, 404 for not found, 409 for conflicts, 422 for unprocessable entities, 429 for rate limits, and 500 for server errors.\"\n },\n {\n \"question\": \"How do I handle errors in React Query?\",\n \"answer\": \"Use the onError callback in useMutation or useQuery options. Pass ApiResponseError instances to show user-friendly messages. Use toast.error() or other notification systems to display errors to users.\"\n }\n ]\n },\n \"file_structure\": [\n {\n \"name\": \"scripts\",\n \"type\": \"dir\",\n \"path\": \"scripts\",\n \"children\": [\n {\n \"name\": \"verify.py\",\n \"type\": \"file\",\n \"path\": \"scripts/verify.py\",\n \"lines\": 62\n }\n ]\n },\n {\n \"name\": \"SKILL.md\",\n \"type\": \"file\",\n \"path\": \"SKILL.md\",\n \"lines\": 570\n }\n ]\n}\n","content_type":"application/json; charset=utf-8","language":"json","size":11051,"content_sha256":"0c7b51cb3cdbb772b7fe42c723f6d1b8610bf056660a81b40b46124f93154353"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Error Handling Skill","type":"text"}]},{"type":"paragraph","content":[{"text":"Expert structured error handling for FastAPI backends and React/Next.js frontends with consistent error messages and logging.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Reference","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Pattern","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Backend","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Frontend","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Custom exception","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"class FeeNotPaidError(AppException)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"N/A","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Try-catch","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"try: ... except SpecificError:","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"try { } catch (e) { }","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Global handler","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"@app.exception_handler","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ErrorBoundary","type":"text","marks":[{"type":"code_inline"}]},{"text":" component","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"User message","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"detail","type":"text","marks":[{"type":"code_inline"}]},{"text":" field in response","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Toast/Snackbar","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Error logging","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"logger.error(...)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Console/Sentry","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Custom Exceptions (Backend)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Base Exception Hierarchy","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"# backend/app/errors/exceptions.py\nfrom fastapi import HTTPException\nfrom typing import Any\n\n\nclass AppException(HTTPException):\n \"\"\"Base application exception with user-friendly messages.\"\"\"\n\n def __init__(\n self,\n status_code: int,\n detail: str,\n user_message: str,\n headers: dict[str, Any] | None = None,\n ):\n self.user_message = user_message\n super().__init__(status_code=status_code, detail=detail, headers=headers)\n\n\nclass NotFoundError(AppException):\n \"\"\"Resource not found (404).\"\"\"\n\n def __init__(self, resource: str, identifier: str):\n super().__init__(\n status_code=404,\n detail=f\"{resource} with id '{identifier}' not found\",\n user_message=f\"{resource} not found. Please check and try again.\",\n )\n\n\nclass ValidationError(AppException):\n \"\"\"Validation failed (400).\"\"\"\n\n def __init__(self, field: str, reason: str):\n super().__init__(\n status_code=400,\n detail=f\"Validation error for field '{field}': {reason}\",\n user_message=f\"Invalid value for {field}. {reason}\",\n )\n\n\nclass UnauthorizedError(AppException):\n \"\"\"Authentication required (401).\"\"\"\n\n def __init__(self, reason: str = \"Authentication required\"):\n super().__init__(\n status_code=401,\n detail=reason,\n user_message=\"Please log in to continue.\",\n headers={\"WWW-Authenticate\": \"Bearer\"},\n )\n\n\nclass ForbiddenError(AppException):\n \"\"\"Permission denied (403).\"\"\"\n\n def __init__(self, action: str):\n super().__init__(\n status_code=403,\n detail=f\"Permission denied for action: {action}\",\n user_message=f\"You don't have permission to {action}.\",\n )\n\n\nclass ConflictError(AppException):\n \"\"\"Resource conflict (409).\"\"\"\n\n def __init__(self, resource: str, reason: str):\n super().__init__(\n status_code=409,\n detail=f\"Conflict for {resource}: {reason}\",\n user_message=f\"{resource} conflict. {reason}\",\n )\n\n\nclass RateLimitError(AppException):\n \"\"\"Too many requests (429).\"\"\"\n\n def __init__(self, retry_after: int = 60):\n super().__init__(\n status_code=429,\n detail=f\"Rate limit exceeded. Retry after {retry_after} seconds.\",\n user_message=\"Too many requests. Please wait a moment and try again.\",\n headers={\"Retry-After\": str(retry_after)},\n )","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Domain-Specific Exceptions","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"# backend/app/errors/domains.py\nfrom .exceptions import NotFoundError, ValidationError, ConflictError\n\n\nclass StudentNotFoundError(NotFoundError):\n def __init__(self, student_id: int):\n super().__init__(resource=\"Student\", identifier=str(student_id))\n\n\nclass FeeNotPaidError(ConflictError):\n def __init__(self, student_id: int, amount_due: float):\n super().__init__(\n resource=\"Fee\",\n reason=f\"Student {student_id} has unpaid fee of ${amount_due:.2f}\",\n )\n\n\nclass AttendanceAlreadyMarkedError(ConflictError):\n def __init__(self, student_id: int, date: str):\n super().__init__(\n resource=\"Attendance\",\n reason=f\"Attendance already marked for student {student_id} on {date}\",\n )\n\n\nclass InvalidGradeError(ValidationError):\n def __init__(self, grade: str, valid_grades: list[str]):\n super().__init__(\n field=\"grade\",\n reason=f\"'{grade}' is not valid. Must be one of: {', '.join(valid_grades)}\",\n )\n\n\nclass InsufficientBalanceError(ValidationError):\n def __init__(self, required: float, available: float):\n super().__init__(\n field=\"amount\",\n reason=f\"Insufficient balance. Required: ${required:.2f}, Available: ${available:.2f}\",\n )","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Try-Catch Patterns","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Narrow Try Blocks","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"# GOOD: Specific exception, narrow scope\ntry:\n student = await get_student_by_id(student_id)\nexcept StudentNotFoundError:\n raise StudentNotFoundError(student_id)\n\n# BAD: Too broad, catches everything\ntry:\n student = await get_student_by_id(student_id)\n calculate_fees(student)\n send_notification(student)\n update_records(student)\nexcept Exception:\n pass # Swallowed!","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Cleanup with Finally","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"import asyncio\nfrom contextlib import asynccontextmanager\n\n\n@asynccontextmanager\nasync def database_connection():\n conn = await get_db_connection()\n try:\n yield conn\n except Exception as e:\n await conn.rollback()\n raise\n finally:\n await conn.close()\n\n\nasync def transfer_funds(from_account: int, to_account: int, amount: float):\n async with database_connection() as conn:\n try:\n await conn.execute(\"UPDATE accounts SET balance = balance - ? WHERE id = ?\", amount, from_account)\n await conn.execute(\"UPDATE accounts SET balance = balance + ? WHERE id = ?\", amount, to_account)\n await conn.commit()\n except InsufficientBalanceError:\n await conn.rollback()\n raise","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Error Logging (Backend)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Structured JSON Logging","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"# backend/app/core/logger.py\nimport logging\nimport json\nfrom datetime import datetime\nfrom typing import Any\nfrom contextvars import ContextVar\nimport traceback\n\n# Correlation ID for request tracing\ncorrelation_id_var: ContextVar[str] = ContextVar(\"correlation_id\", default=\"\")\n\n\nclass StructuredLogger:\n \"\"\"Structured JSON logger with correlation IDs.\"\"\"\n\n def __init__(self, name: str):\n self.logger = logging.getLogger(name)\n self.logger.setLevel(logging.INFO)\n\n def _log_record(self, level: str, message: str, extra: dict[str, Any] = None):\n record = {\n \"timestamp\": datetime.utcnow().isoformat(),\n \"level\": level,\n \"message\": message,\n \"correlation_id\": correlation_id_var.get(),\n **(extra or {}),\n }\n return json.dumps(record)\n\n def info(self, message: str, **extra):\n self.logger.info(self._log_record(\"INFO\", message, extra))\n\n def warning(self, message: str, **extra):\n self.logger.warning(self._log_record(\"WARNING\", message, extra))\n\n def error(self, message: str, error: Exception = None, **extra):\n log_extra = {**extra}\n if error:\n log_extra[\"error_type\"] = type(error).__name__\n log_extra[\"error_message\"] = str(error)\n log_extra[\"stack_trace\"] = traceback.format_exc()\n self.logger.error(self._log_record(\"ERROR\", message, log_extra))\n\n\nlogger = StructuredLogger(__name__)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Using the Logger","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"from app.core.logger import logger, correlation_id_var\n\n\nasync def get_student(student_id: int) -> Student:\n logger.info(\"Fetching student\", student_id=student_id)\n\n try:\n student = await db.get_student(student_id)\n logger.info(\"Student fetched successfully\", student_id=student_id, has_fees=bool(student.fees))\n return student\n except StudentNotFoundError:\n logger.warning(\"Student not found\", student_id=student_id)\n raise\n except Exception as e:\n logger.error(\"Unexpected error fetching student\", error=e, student_id=student_id)\n raise # Re-raise for handler","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Global Error Handler (Backend)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"# backend/app/middleware/error_handler.py\nfrom fastapi import Request, FastAPI\nfrom fastapi.responses import JSONResponse\nfrom app.errors.exceptions import AppException\nfrom app.core.logger import logger\n\n\ndef setup_error_handlers(app: FastAPI):\n @app.exception_handler(AppException)\n async def app_exception_handler(request: Request, exc: AppException):\n logger.error(\n \"App exception occurred\",\n error=exc,\n status_code=exc.status_code,\n path=request.url.path,\n )\n return JSONResponse(\n status_code=exc.status_code,\n content={\n \"error\": {\n \"code\": exc.__class__.__name__,\n \"message\": exc.user_message,\n \"internal\": exc.detail, # Only in debug mode\n }\n },\n headers=exc.headers,\n )\n\n @app.exception_handler(Exception)\n async def general_exception_handler(request: Request, exc: Exception):\n logger.error(\n \"Unhandled exception\",\n error=exc,\n path=request.url.path,\n method=request.method,\n )\n return JSONResponse(\n status_code=500,\n content={\n \"error\": {\n \"code\": \"INTERNAL_ERROR\",\n \"message\": \"Something went wrong. Please try again later.\",\n }\n },\n )","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Frontend Error Handling","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Error Types","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"// frontend/lib/errors.ts\nexport interface ApiError {\n error: {\n code: string;\n message: string;\n internal?: string;\n };\n}\n\nexport class ApiResponseError extends Error {\n code: string;\n userMessage: string;\n internalMessage?: string;\n statusCode: number;\n\n constructor(error: ApiError[\"error\"], statusCode: number) {\n super(error.message);\n this.name = \"ApiResponseError\";\n this.code = error.code;\n this.userMessage = error.message;\n this.internalMessage = error.internal;\n this.statusCode = statusCode;\n }\n}","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Error Mapper","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"// frontend/lib/errorMapper.ts\nimport { ApiResponseError } from \"./errors\";\n\nexport function getUserMessage(error: unknown): string {\n if (error instanceof ApiResponseError) {\n return error.userMessage;\n }\n\n if (error instanceof Error) {\n // Network errors\n if (error.name === \"TypeError\" && error.message.includes(\"fetch\")) {\n return \"Unable to connect to server. Please check your internet connection.\";\n }\n\n // Unexpected errors\n return \"An unexpected error occurred. Please try again.\";\n }\n\n return \"An unknown error occurred.\";\n}\n\nexport function getErrorTitle(error: unknown): string {\n if (error instanceof ApiResponseError) {\n switch (error.statusCode) {\n case 401:\n return \"Authentication Required\";\n case 403:\n return \"Access Denied\";\n case 404:\n return \"Not Found\";\n case 409:\n return \"Conflict\";\n case 422:\n return \"Validation Error\";\n case 429:\n return \"Rate Limited\";\n case 500:\n return \"Server Error\";\n default:\n return \"Error\";\n }\n }\n\n return \"Error\";\n}","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"API Client with Error Handling","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"// frontend/lib/api.ts\nimport { ApiResponseError } from \"./errors\";\nimport { getUserMessage } from \"./errorMapper\";\n\ninterface RequestOptions extends RequestInit {\n params?: Record\u003cstring, string>;\n}\n\nclass ApiClient {\n private baseUrl: string;\n\n constructor(baseUrl: string = \"/api/v1\") {\n this.baseUrl = baseUrl;\n }\n\n async request\u003cT>(endpoint: string, options: RequestOptions = {}): Promise\u003cT> {\n const url = new URL(`${this.baseUrl}${endpoint}`, window.location.origin);\n\n if (options.params) {\n Object.entries(options.params).forEach(([key, value]) => {\n url.searchParams.append(key, value);\n });\n }\n\n const response = await fetch(url.toString(), {\n ...options,\n headers: {\n \"Content-Type\": \"application/json\",\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n\n throw new ApiResponseError(\n {\n code: errorData.error?.code || \"HTTP_ERROR\",\n message: errorData.error?.message || response.statusText,\n internal: errorData.error?.internal,\n },\n response.status\n );\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return response.json() as Promise\u003cT>;\n }\n\n get\u003cT>(endpoint: string, params?: Record\u003cstring, string>): Promise\u003cT> {\n return this.request\u003cT>(endpoint, { method: \"GET\", params });\n }\n\n post\u003cT>(endpoint: string, body: unknown): Promise\u003cT> {\n return this.request\u003cT>(endpoint, { method: \"POST\", body: JSON.stringify(body) });\n }\n\n put\u003cT>(endpoint: string, body: unknown): Promise\u003cT> {\n return this.request\u003cT>(endpoint, { method: \"PUT\", body: JSON.stringify(body) });\n }\n\n delete\u003cT>(endpoint: string): Promise\u003cT> {\n return this.request\u003cT>(endpoint, { method: \"DELETE\" });\n }\n}\n\nexport const api = new ApiClient();","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"React Query with Error Handling","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"// frontend/hooks/useApi.ts\nimport { useQuery, useMutation, UseQueryOptions } from \"@tanstack/react-query\";\nimport { api } from \"../lib/api\";\nimport { ApiResponseError } from \"../lib/errors\";\nimport { toast } from \"sonner\";\n\nexport function useFetch\u003cT>(\n key: string[],\n endpoint: string,\n options?: Partial\u003cUseQueryOptions\u003cT>>\n) {\n return useQuery({\n queryKey: key,\n queryFn: () => api.get\u003cT>(endpoint),\n ...options,\n onError: (error: unknown) => {\n if (error instanceof ApiResponseError) {\n toast.error(error.userMessage);\n } else {\n toast.error(\"Failed to fetch data\");\n }\n },\n });\n}\n\nexport function useMutationWithError\u003cT, V>(\n mutationFn: (variables: V) => Promise\u003cT>,\n successMessage: string,\n onSuccess?: (data: T) => void\n) {\n return useMutation({\n mutationFn,\n onSuccess: (data) => {\n toast.success(successMessage);\n onSuccess?.(data);\n },\n onError: (error: unknown) => {\n if (error instanceof ApiResponseError) {\n toast.error(error.userMessage);\n } else {\n toast.error(\"An error occurred. Please try again.\");\n }\n },\n });\n}","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quality Checklist","type":"text"}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"No swallowed errors","type":"text","marks":[{"type":"strong"}]},{"text":": Every exception is either logged or surfaced to user","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Internal vs user message separation","type":"text","marks":[{"type":"strong"}]},{"text":": Technical details in ","type":"text"},{"text":"detail","type":"text","marks":[{"type":"code_inline"}]},{"text":", user-friendly in ","type":"text"},{"text":"user_message","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"4xx vs 5xx correctly mapped","type":"text","marks":[{"type":"strong"}]},{"text":": Client errors (4xx) vs server errors (5xx)","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"No PII in logs","type":"text","marks":[{"type":"strong"}]},{"text":": Never log passwords, tokens, personal data","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Correlation IDs","type":"text","marks":[{"type":"strong"}]},{"text":": Track errors across services with request IDs","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Consistent error format","type":"text","marks":[{"type":"strong"}]},{"text":": Same structure for all API errors","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Error Response Format","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"error\": {\n \"code\": \"STUDENT_NOT_FOUND\",\n \"message\": \"Student not found. Please check and try again.\",\n \"internal\": \"Student with id '12345' not found\"\n }\n}","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Integration Points","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Skill","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Integration","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"@fastapi-app","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Global exception handlers in main.py","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"@api-route-design","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Proper status codes in responses","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"@jwt-auth","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"UnauthorizedError for auth failures","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"@db-migration","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Handle migration errors gracefully","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"@env-config","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Configuration validation errors","type":"text"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"error-handling","author":"@skillopedia","source":{"stars":336,"repo_name":"marketplace","origin_url":"https://github.com/aiskillstore/marketplace/blob/HEAD/skills/awais68/error-handling/SKILL.md","repo_owner":"aiskillstore","body_sha256":"e8fe6bc62af28339e6cff649ed92eb0f8c0c64aacb4fbb19df993235ccdda896","cluster_key":"558fe66c787a0e6c3a95dacff29463de7e01de233313af03f9325dccf2473ffb","clean_bundle":{"format":"clean-skill-bundle-v1","source":"aiskillstore/marketplace/skills/awais68/error-handling/SKILL.md","attachments":[{"id":"c99fd331-f337-51c8-81c4-62567393b1d7","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c99fd331-f337-51c8-81c4-62567393b1d7/attachment.py","path":"scripts/verify.py","size":1902,"sha256":"2506808ccdd732a3b016fbdb5bb7d919d31c867996cd7b28beb5438bf2329ea2","contentType":"text/x-python; charset=utf-8"},{"id":"3f3588f3-3da4-5956-a679-74f4fa2b6081","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/3f3588f3-3da4-5956-a679-74f4fa2b6081/attachment.json","path":"skill-report.json","size":11051,"sha256":"0c7b51cb3cdbb772b7fe42c723f6d1b8610bf056660a81b40b46124f93154353","contentType":"application/json; charset=utf-8"}],"bundle_sha256":"184abd6be6ff84f1bd81b419a2523d1f7b6b9e38f0a6a84237d31c05dc4043b5","attachment_count":2,"text_attachments":2,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"skills/awais68/error-handling/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"web-development","category_label":"Web"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"web-development","import_tag":"clean-skills-v1","description":"Use when implementing structured error handling in backend or frontend code.\nTriggers for: try-catch patterns, custom exception classes, global error handlers,\nerror logging, user-friendly error messages, or API error responses.\nNOT for: business logic validation (use domain exceptions) or unrelated error types.\n"}},"renderedAt":1782982029021}

Error Handling Skill Expert structured error handling for FastAPI backends and React/Next.js frontends with consistent error messages and logging. Quick Reference | Pattern | Backend | Frontend | |---------|---------|----------| | Custom exception | | N/A | | Try-catch | | | | Global handler | | component | | User message | field in response | Toast/Snackbar | | Error logging | | Console/Sentry | Custom Exceptions (Backend) Base Exception Hierarchy Domain-Specific Exceptions Try-Catch Patterns Narrow Try Blocks Cleanup with Finally Error Logging (Backend) Structured JSON Logging Using the Log…