Error Handling and Logging
Relevant source files
The following files were used as context for generating this wiki page:
The application employs a multi-layered approach to error handling and logging, designed to catch and manage errors gracefully at different levels of the software stack. This system ensures a stable user experience by preventing crashes, providing clear user feedback, and offering robust logging for developers.
The strategy combines a React component-level boundary for UI rendering errors, a centralized service for handling synchronous and asynchronous exceptions, and a dedicated logging utility for development and monitoring. This ensures that errors are not only caught but also categorized, logged, and presented to the user in an appropriate manner.
1. High-Level Architecture
The error handling architecture consists of three main components:
ErrorBoundaryComponent: A React component that wraps major parts of the UI to catch rendering errors and display a fallback screen.ErrorHandlerService: A singleton service for programmatic error handling, logging, and user notification.LoggerUtility: A service for structured, level-based console logging during development, which is automatically disabled in production.
This diagram illustrates the general flow of an error through the system.
2. UI Error Boundary (ErrorBoundary.js)
The ErrorBoundary is a React class component that uses getDerivedStateFromError and componentDidCatch to handle JavaScript errors in its child component tree. When an error is caught, it renders a fallback UI instead of the crashing component tree.
Sources: src/shared/components/ErrorBoundary.js
Key Features
- Fallback UI: Displays a user-friendly screen with an "Oops! Something went wrong" message.
- Development Details: In development mode (
__DEV__), it displays detailed error information and a stack trace to aid debugging. - User Actions: Provides a "Try Again" button to reset the error state and attempt to re-render. A "Dismiss" button is also available in development.
- Error Logging: Logs the error and component stack information to the console via
console.error.
The fallback UI is styled with a LinearGradient and uses icons from @expo/vector-icons.
// src/shared/components/ErrorBoundary.js:19-27
componentDidCatch(error, errorInfo) {
// Log error to error reporting service
console.error('ErrorBoundary caught an error:', error, errorInfo);
this.setState({
error,
errorInfo,
});
}
Sources: src/shared/components/ErrorBoundary.js:19-27
3. Centralized ErrorHandler Service
The errorHandler.js file exports a singleton instance of the ErrorHandler class, which serves as the core of the application's programmatic error management.
Sources: src/services/errorHandler.js
3.1. AppError Class
A custom error class, AppError, is used to standardize errors throughout the application. It includes a type, message, the originalError, and a timestamp.
Sources: src/services/errorHandler.js:41-47
3.2. Error Types
The service defines a set of standardized error types to categorize issues.
| Type | Description |
|---|---|
NETWORK | Network error, e.g., no internet connection. |
AUTH | Authentication failure. |
VALIDATION | Invalid user input. |
NOT_FOUND | Resource not found (e.g., 404). |
SERVER | Server-side error (e.g., 5xx). |
STORAGE | Error related to local data storage. |
PERMISSION | Necessary permissions were denied by the user. |
UNKNOWN | An unexpected or uncategorized error. |
| Sources: src/services/errorHandler.js:8-17 |
3.3. Core Methods
The ErrorHandler class provides several methods for processing errors.
handleError(error, options)
This is the main method for processing an error. It logs the error and can display a UI notification.
Options:
| Parameter | Type | Default | Description |
|---|---|---|---|
showAlert | boolean | true | If true, shows a native Alert. |
showNotification | boolean | false | If true, uses useNotificationStore to show an in-app notification. |
customMessage | string | null | A custom message to override the error's default message. |
onDismiss | function | null | A callback function for when the Alert is dismissed. |
| Sources: src/services/errorHandler.js:107-112 |
Specialized Handlers
The service includes handlers for common error scenarios:
handleApiError(error): Interprets HTTP error responses (4xx, 5xx) and maps them to the appropriateERROR_TYPES.handleStorageError(error, operation): Handles local storage failures.handleValidationError(errors): Formats validation error messages.handlePermissionError(permission): Handles denied permission requests.
Sources: src/services/errorHandler.js:139-204
This sequence diagram shows how an API error is handled.
Sources: src/services/errorHandler.js:139-162
3.4. Helper Wrappers
To simplify implementation, the service provides wrapper functions:
tryCatch(fn, errorHandler): A simple async wrapper that executes a function and calls a handler on failure.withAsyncErrorHandler(fn): A higher-order function that wraps an async function in a try-catch block, automatically callingerrorHandler.handleErroron failure.
Sources: src/services/errorHandler.js:223-242
4. Logger Service (logger.js)
A professional logger service was created to replace widespread console.log statements. It provides structured, level-based logging that is automatically managed based on the environment.
Sources: SECURITY_AND_CLEANUP_FIXES.md
Features
- Log Levels: Supports
debug,info,warn, anderrorlevels. - Production Safe: Automatically disables logging in production builds to avoid leaking information and impacting performance.
- Structured Output: Adds timestamps and context to log entries.
- Developer Experience: Features color-coded output (for web), log grouping (
group,groupEnd), and timing functions (time,timeEnd).
Usage Example
import logger from './src/core/utils/logger';
// Basic logging
logger.info('User logged in', { userId: 123 });
logger.error('Error occurred', error);
// Grouping
logger.group('API Calls');
logger.info('GET /users');
logger.groupEnd();
// Timing
logger.time('API Call');
await fetchData();
logger.timeEnd('API Call');
Sources: SECURITY_AND_CLEANUP_FIXES.md
5. Practical Implementation
The codebase shows a clear pattern of wrapping potentially failing operations, especially asynchronous ones, in try-catch blocks.
Camera and Media Library Error Handling
In ARViewScreen.js, all interactions with expo-camera and expo-media-library are wrapped to handle both permission denials and operational failures. Errors are reported to the user via Alert.alert.
// Example from AUDIT_FIXES_SUMMARY.md
try {
const photo = await cameraRef.current.takePictureAsync({...});
// Process photo
} catch (error) {
console.error('Error taking picture:', error);
Alert.alert('Camera Error', 'Failed to capture photo. Please try again.');
}
Sources: AUDIT_FIXES_SUMMARY.md
Environment Variable Validation
The application validates its environment configuration on startup. If the Supabase URL or key is missing or malformed, it logs a detailed, formatted error message to the console in development to guide the developer in fixing the setup.
Sources: src/core/config/env.config.js:58-86
Fixing Faulty Error Logic
An example of improving error handling can be seen in the fix for the export service. The original code incorrectly checked for result.success on an object, when the function actually returned a URI string on success. The fix was to check for the presence of the fileUri string instead, ensuring the success alert is correctly triggered.
// Before
const result = await exportService.exportAsPNG(...);
if (result.success) { // Incorrect check
Alert.alert('Success', 'Exported as PNG!');
}
// After
const fileUri = await exportService.exportAsPNG(...);
if (fileUri) { // Correct check
Alert.alert('Success', 'Exported as PNG!');
}
Sources: CRITICAL_BUGS_FIXED.md