# Error Handling & Debugging This guide covers error handling patterns and debugging strategies in the framework. ## Exception Handling All custom exceptions in the framework must extend `FrameworkException` to ensure consistent error handling, logging, and recovery mechanisms. ### The FrameworkException System The framework provides a sophisticated exception system with: - **ExceptionContext**: Rich context information for debugging - **ErrorCode**: Categorized error codes with recovery hints - **RetryAfter**: Support for recoverable operations - **Fluent Interface**: Easy context building ### Creating Custom Exceptions ```php namespace App\Domain\User\Exceptions; use App\Framework\Exception\FrameworkException; use App\Framework\Exception\ErrorCode; use App\Framework\Exception\ExceptionContext; final class UserNotFoundException extends FrameworkException { public static function byId(UserId $id): self { return self::create( ErrorCode::ENTITY_NOT_FOUND, "User with ID '{$id->toString()}' not found" )->withData([ 'user_id' => $id->toString(), 'search_type' => 'by_id' ]); } public static function byEmail(Email $email): self { $context = ExceptionContext::forOperation('user.lookup', 'UserRepository') ->withData(['email' => $email->getMasked()]); return self::fromContext( "User with email not found", $context, ErrorCode::ENTITY_NOT_FOUND ); } } ``` ### Using ErrorCode Enum ```php // The framework provides predefined error codes: ErrorCode::DB_CONNECTION_FAILED // Database errors ErrorCode::AUTH_TOKEN_EXPIRED // Authentication errors ErrorCode::VAL_BUSINESS_RULE_VIOLATION // Validation errors ErrorCode::HTTP_RATE_LIMIT_EXCEEDED // HTTP errors ErrorCode::SEC_CSRF_TOKEN_INVALID // Security errors // Using error codes in exceptions: throw FrameworkException::create( ErrorCode::DB_QUERY_FAILED, "Failed to execute user query" )->withContext( ExceptionContext::forOperation('user.find', 'UserRepository') ->withData(['query' => 'SELECT * FROM users WHERE id = ?']) ->withDebug(['bind_params' => [$userId]]) ); ``` ### Exception Context Building ```php // Method 1: Using factory methods $exception = FrameworkException::forOperation( 'payment.process', 'PaymentService', 'Payment processing failed', ErrorCode::PAYMENT_GATEWAY_ERROR )->withData([ 'amount' => $amount->toArray(), 'gateway' => 'stripe', 'customer_id' => $customerId ])->withMetadata([ 'attempt' => 1, 'idempotency_key' => $idempotencyKey ]); // Method 2: Building context separately $context = ExceptionContext::empty() ->withOperation('order.validate', 'OrderService') ->withData([ 'order_id' => $orderId, 'total' => $total->toDecimal() ]) ->withDebug([ 'validation_rules' => ['min_amount', 'max_items'], 'failed_rule' => 'min_amount' ]); throw FrameworkException::fromContext( 'Order validation failed', $context, ErrorCode::VAL_BUSINESS_RULE_VIOLATION ); ``` ### Recoverable Exceptions ```php // Creating recoverable exceptions with retry hints final class RateLimitException extends FrameworkException { public static function exceeded(int $retryAfter): self { return self::create( ErrorCode::HTTP_RATE_LIMIT_EXCEEDED, 'API rate limit exceeded' )->withRetryAfter($retryAfter) ->withData(['retry_after_seconds' => $retryAfter]); } } // Using in code try { $response = $apiClient->request($endpoint); } catch (RateLimitException $e) { if ($e->isRecoverable()) { $waitTime = $e->getRetryAfter(); // Schedule retry after $waitTime seconds } throw $e; } ``` ### Exception Categories ```php // Check exception category for handling strategies try { $result = $operation->execute(); } catch (FrameworkException $e) { if ($e->isCategory('AUTH')) { // Handle authentication errors return $this->redirectToLogin(); } if ($e->isCategory('VAL')) { // Handle validation errors return $this->validationErrorResponse($e); } if ($e->isErrorCode(ErrorCode::DB_CONNECTION_FAILED)) { // Handle specific database connection errors $this->notifyOps($e); } throw $e; } ``` ### Simple Exceptions for Quick Use ```php // When you don't need the full context system throw FrameworkException::simple('Quick error message'); // With previous exception } catch (\PDOException $e) { throw FrameworkException::simple( 'Database operation failed', $e, 500 ); } ``` ### Exception Data Sanitization The framework automatically sanitizes sensitive data in exceptions: ```php // Sensitive keys are automatically redacted $exception->withData([ 'username' => 'john@example.com', 'password' => 'secret123', // Will be logged as '[REDACTED]' 'api_key' => 'sk_live_...' // Will be logged as '[REDACTED]' ]); ``` ### Best Practices 1. **Always extend FrameworkException** for custom exceptions 2. **Use ErrorCode enum** for categorizable errors 3. **Provide rich context** with operation, component, and data 4. **Use factory methods** for consistent exception creation 5. **Sanitize sensitive data** (automatic for common keys) 6. **Make exceptions domain-specific** (UserNotFoundException vs generic NotFoundException) 7. **Include recovery hints** for recoverable errors ## Logging Best Practices TODO: Document logging patterns and levels ## Debug Strategies TODO: Document debugging approaches and tools ## Error Recovery Patterns TODO: Document error recovery and graceful degradation ## Common Error Scenarios TODO: List common errors and solutions