Error Handling Architecture
Comprehensive error handling patterns and custom error classes for the application
Error Handling Architecture
Overview
This document defines our error handling strategy, including custom error classes, error propagation patterns, and user-friendly error messages. Consistent error handling improves debugging, user experience, and app reliability. Our approach implements a hierarchical class structure with propagation mechanisms to ensure errors are caught, processed, and presented appropriately throughout the application.
Purpose & Scope
This guide outlines:
- The custom error class hierarchy used across the application.
- Standardized error codes and HTTP status mappings.
- Patterns for handling and transforming errors at different layers (API, Domain, UI).
- Best practices for logging, monitoring, and displaying errors to users.
Prerequisites
To effectively understand and implement the error handling patterns described here, readers should be familiar with:
- JavaScript Error Handling: Fundamentals of
Errorobjects,try...catchblocks, and error propagation in JavaScript/TypeScript. - HTTP Status Codes: A basic understanding of common HTTP status codes and their meanings (e.g., 2xx, 4xx, 5xx series).
- API Client (Recommended): Familiarity with the project's API Client Architecture can be helpful to understand how API errors are initially caught and processed, especially regarding the
handleApiErrorutility. - Logging/Monitoring Tools (Contextual): While not mandatory for understanding the error classes, awareness of any project-specific logging or crash reporting tools (e.g., Crashlytics, Sentry) will provide context for the error logging examples.
Constants Organization
Our error handling system relies on a structured set of constants for error codes, HTTP status codes, and API configurations. These are typically organized as follows:
index.ts serves as the barrel file for exporting all constants from this module.
Status Code Constants
Error Class Hierarchy
Base Error Class
Network Error Classes
No Internet Connection Error
Request Timeout Error
API Response Errors
Authentication Error Classes
Validation Error Classes
Business Logic Errors
Storage Errors
Error Handling Flow
Error Handling in API Client
Error Handling in Domain APIs
Error Handling in UI Components
Global Error Boundary
Error Logging and Monitoring
Error Message Localization
Testing Error Scenarios
Note: These error testing strategies integrate with our broader testing approach outlined in the testing document. The error tests focus on unit testing individual error classes and integration testing error handling mechanisms, complementing the end-to-end tests that verify complete user flows during error conditions.
Best Practices
To maintain a robust and understandable error handling system, we recommend adhering to the following best practices:
- (Do ✅) Use Specific Error Classes: Create and use distinct error classes that inherit from
ExtendableErrorfor different categories of errors (e.g.,NetworkError,AuthenticationError,ValidationError). This enables more preciseinstanceofchecks, targeted error handling logic, and clearer intent in your code. - (Do ✅) Include Rich Context: When throwing or logging errors, ensure they carry as much relevant context as possible (e.g.,
statusCode,errorCode,endpoint, relevant data). This significantly aids in debugging and understanding the root cause of issues. - (Do ✅) Provide User-Friendly Messages: For errors that will be displayed to the user, craft messages that are clear, concise, and actionable, guiding them on what to do next or what went wrong in understandable terms. Avoid exposing raw technical error details directly to users.
- (Do ✅) Log Errors Comprehensively: Implement thorough error logging with sufficient context (as per point 2) at appropriate layers. This is crucial for monitoring application health, identifying patterns, and diagnosing problems in development and production.
- (Do ✅) Handle Errors Gracefully: Always ensure there are fallback mechanisms (like Error Boundaries in UI) to catch unexpected errors and prevent application crashes, providing a more stable user experience.
- (Do ✅) Test Error Paths Thoroughly: Write unit and integration tests specifically for error scenarios to verify that your error handling logic behaves as expected under various failure conditions.
- (Do ✅) Localize User-Facing Error Messages: If your application supports multiple languages, ensure that error messages displayed to users are properly localized for a better user experience globally.
Design Principles
Core Architectural Principles
-
Hierarchical Organization
- Error classes follow a clear, logical inheritance hierarchy
- Common behavior implemented in base classes
- Specific behavior in specialized classes
-
Contextual Information
- Errors carry detailed context about what went wrong
- Status codes mapped to semantic error types
- Supporting data attached to aid debugging
-
Error Propagation
- Errors are transformed at system boundaries
- Domain-specific errors remain isolated to their domains
- Central error handling manages common error patterns
Trade-offs and Design Decisions
| Decision | Benefits | Drawbacks | Rationale |
|---|---|---|---|
| Custom error classes | Type-safe handling, rich context | More code to maintain | Enables precise error handling patterns and better debugging |
| Centralized error handler | Consistent handling, DRY | Potential single point of failure | Ensures consistent error transformation across the application |
| Error boundaries at component level | Graceful recovery, isolation | More complex component structure | Prevents entire app crashes due to UI errors |
| Typed error codes | Static analysis, auto-completion | Overhead in maintenance | Makes error handling more robust and less prone to typos |
Constraints and Considerations
- All error messages must be localizable
- Error handling must work in offline scenarios
- Performance impact of try/catch blocks must be considered
- Sensitive information must never appear in error messages or logs
Implementation Considerations
Performance Implications
- Error Creation: Creating error objects with full stack traces can be expensive; optimize for production
- Try/Catch Blocks: Excessive try/catch can affect JavaScript engine optimization
- Error Transformation: Complex transformations should be avoided in critical paths
- Logging: Asynchronous logging prevents UI thread blocking
Security Considerations
- Sensitive Data: Never include auth tokens, passwords or PII in error messages or logs
- User Messages: External-facing error messages should be sanitized
- Stack Traces: Never expose stack traces to end users
- Input Validation: Validate all inputs to prevent injection attacks through error messages
Scalability Aspects
- Error Categories: The system supports adding new error categories without modifying core logic
- Error Routing: Error handling architecture supports routing errors to different handlers
- Domain Isolation: Domain-specific errors don't leak across boundaries
- Monitoring: Error tracking scales with automatically grouped similar errors
Summary
This comprehensive error handling architecture provides:
- Clear error class hierarchy
- Specific errors for different scenarios
- Centralized error handling
- User-friendly error messages
- Proper error propagation
- Error logging and monitoring
- Localization support
Related Documents
- API Client Architecture - Details on API client implementation
- Authentication Architecture - Details on authentication flows and how
authEventslikeauth:sessionExpiredare emitted. - API Client with Request Queueing - How the API client handles 401s internally with token refresh and request queueing before an error might propagate to the general
handleApiError. - Communication Patterns - Error events propagation
- testing - Testing error scenarios
- monitoring-analytics