--- name: ajax-error-handling description: Handling Ajax errors in RSX including response formats, error codes, client-side error display, and Form_Utils. Use when implementing error handling for Ajax calls, working with response_error(), form validation errors, or debugging Ajax failures. --- # RSX Ajax Error Handling ## Response Architecture RSX returns **HTTP 200 for ALL Ajax responses** (success and errors). Success/failure is encoded in the response body via `_success` field. ```javascript // Success response { "_success": true, "_ajax_return_value": { /* your data */ } } // Error response { "_success": false, "error_code": "validation|not_found|unauthorized|auth_required|fatal", "reason": "User-friendly message", "metadata": { /* field errors for validation */ } } ``` **Rationale**: Batch requests need uniform status codes. Always get parseable response body. Non-200 only means "couldn't reach PHP at all". --- ## Error Codes | Constant | Purpose | |----------|---------| | `Ajax::ERROR_VALIDATION` | Field validation failures | | `Ajax::ERROR_NOT_FOUND` | Resource not found | | `Ajax::ERROR_UNAUTHORIZED` | User lacks permission | | `Ajax::ERROR_AUTH_REQUIRED` | User not logged in | | `Ajax::ERROR_FATAL` | Uncaught PHP exceptions | Constants available in both PHP (`Ajax::ERROR_*`) and JavaScript (`Ajax.ERROR_*`). --- ## Server-Side: Returning Errors Use `response_error()` helper - never return `_success` manually: ```php #[Ajax_Endpoint] public static function save(Request $request, array $params = []) { // Validation error with field-specific messages if (empty($params['email'])) { return response_error(Ajax::ERROR_VALIDATION, [ 'email' => 'Email is required' ]); } // Not found error $user = User_Model::find($params['id']); if (!$user) { return response_error(Ajax::ERROR_NOT_FOUND, 'User not found'); } // Success - just return data (framework wraps it) $user->name = $params['name']; $user->save(); return ['id' => $user->id]; } ``` ### Let Exceptions Bubble Don't wrap database/framework operations in try/catch. Let exceptions bubble to the global handler: ```php // WRONG - Don't catch framework exceptions try { $user->save(); } catch (Exception $e) { return ['error' => $e->getMessage()]; } // CORRECT - Let it throw $user->save(); // Framework catches and formats ``` **When try/catch IS appropriate**: File uploads, external API calls, user input parsing (expected failures). --- ## Client-Side: Handling Errors Ajax.js automatically unwraps responses: - `_success: true` → Promise resolves with `_ajax_return_value` - `_success: false` → Promise rejects with Error object ```javascript try { const user = await User_Controller.get_user(123); console.log(user.name); // Already unwrapped } catch (error) { console.log(error.code); // 'validation', 'not_found', etc. console.log(error.message); // User-displayable message console.log(error.metadata); // Field errors for validation } ``` ### Automatic Error Display Uncaught Ajax errors automatically display via `Modal.error()`: ```javascript // No try/catch - error shows in modal automatically const user = await User_Controller.get_user(123); ``` --- ## Form Error Handling ### With Rsx_Form (Recommended) ```javascript const result = await Modal.form({ title: 'Add User', component: 'User_Form', on_submit: async (form) => { try { const result = await Controller.save(form.vals()); return result; // Success - close modal } catch (error) { await form.render_error(error); return false; // Keep modal open } } }); ``` `form.render_error()` handles all error types: - **validation**: Shows inline field errors + alert for unmatched errors - **fatal/network/auth**: Shows error in form's error container ### With Form_Utils (Non-Rsx_Form) ```javascript try { const result = await Controller.save(form_data); } catch (error) { if (error.code === Ajax.ERROR_VALIDATION) { Form_Utils.apply_form_errors($form, error.metadata); } else { Rsx.render_error(error, '#error_container'); } } ``` `Form_Utils.apply_form_errors()`: - Matches errors to fields by `name` attribute - Adds `.is-invalid` class and inline error text - Shows alert ONLY for errors that couldn't match to fields --- ## Error Display Methods ### Modal.error() - Critical Errors ```javascript await Modal.error(error, 'Operation Failed'); ``` Red danger modal, can stack over other modals. ### Rsx.render_error() - Container Display ```javascript Rsx.render_error(error, '#error_container'); Rsx.render_error(error, this.$sid('error')); ``` Displays error in any container element. --- ## Developer vs Production Mode **Developer Mode** (`IS_DEVELOPER=true`): - Full exception message - File path and line number - SQL queries with parameters - Stack trace (up to 10 frames) **Production Mode**: - Generic message: "An unexpected error occurred" - No technical details exposed - Errors logged server-side --- ## Common Patterns ### Simple Validation ```php #[Ajax_Endpoint] public static function save(Request $request, array $params = []) { $errors = []; if (empty($params['email'])) { $errors['email'] = 'Email is required'; } if (empty($params['name'])) { $errors['name'] = 'Name is required'; } if ($errors) { return response_error(Ajax::ERROR_VALIDATION, $errors); } // ... save logic } ``` ### Check Specific Error Type ```javascript try { const data = await Controller.get_data(id); } catch (error) { if (error.code === Ajax.ERROR_NOT_FOUND) { show_not_found_message(); } else if (error.code === Ajax.ERROR_UNAUTHORIZED) { redirect_to_login(); } else { // Let default handler show modal throw error; } } ``` ## More Information Details: `php artisan rsx:man ajax_error_handling`