Error Handling
WordPress error handling system using the WP_Error class pattern and PHP error/exception handling.
Since: 2.1.0 (WP_Error), 5.2.0 (Fatal Error Handler), 6.7.0 (WP_Exception)
Source: wp-includes/class-wp-error.php, wp-includes/class-wp-exception.php, wp-includes/class-wp-fatal-error-handler.php, wp-includes/error-protection.php
Components
| Component | Description |
|---|---|
| class-wp-error.md | Error container class for returning errors |
| class-wp-exception.md | Base exception class for exceptions |
| class-wp-fatal-error-handler.md | Fatal PHP error handling |
| functions.md | Core error functions (is_wp_error, wp_die, etc.) |
| hooks.md | Actions and filters for error handling |
Philosophy: WP_Error vs Exceptions
WordPress historically uses return-based error handling rather than exception throwing. This design predates modern PHP practices but provides backward compatibility and explicit control flow.
WP_Error Pattern
function my_function() {
if ( $something_wrong ) {
return new WP_Error( 'error_code', 'Error message', array( 'status' => 400 ) );
}
return $success_value;
}
// Caller must check
$result = my_function();
if ( is_wp_error( $result ) ) {
$code = $result->get_error_code();
$message = $result->get_error_message();
// Handle error
}
WP_Exception Pattern (Since 6.7.0)
try {
// Code that might throw
throw new WP_Exception( 'Something went wrong' );
} catch ( WP_Exception $e ) {
// Handle exception
}
When to Use Each
| Use Case | Approach |
|---|---|
| API functions returning data | WP_Error (backward compatible) |
| Constructor failures | WP_Error (stored in property) |
| Validation errors | WP_Error |
| Critical internal errors | WP_Exception |
| Plugin/theme errors | WP_Error preferred |
| Recovery mode integration | WP_Error |
Key Differences
| Aspect | WP_Error | WP_Exception |
|---|---|---|
| Multiple errors | Yes (can store multiple codes/messages) | No (single message) |
| Error data | Yes (arbitrary data per code) | No (use exception properties) |
| Flow control | Explicit check required | Automatic propagation |
| Legacy support | All WordPress versions | 6.7.0+ only |
| Stack trace | No | Yes |
Error Flow
Standard Error Return
Function returns WP_Error
└── Caller checks is_wp_error()
├── TRUE: Handle error (get_error_message, etc.)
└── FALSE: Continue with result
Fatal Error Flow (5.2.0+)
PHP Fatal Error occurs
└── register_shutdown_function() triggers
└── WP_Fatal_Error_Handler::handle()
├── detect_error()
├── should_handle_error()
├── Recovery Mode (if available)
└── display_error_template()
wp_die() Flow
wp_die( $message, $title, $args )
├── Detect request type (Ajax, JSON, XML-RPC, HTML)
├── Apply handler filter (wp_die_handler, wp_die_ajax_handler, etc.)
└── Call appropriate handler
├── _default_wp_die_handler() → HTML page
├── _ajax_wp_die_handler() → Plain text
├── _json_wp_die_handler() → JSON response
├── _xmlrpc_wp_die_handler() → XML-RPC fault
└── _xml_wp_die_handler() → XML response
Error Types Handled
The fatal error handler processes these PHP error types:
E_ERROR– Fatal run-time errorsE_PARSE– Compile-time parse errorsE_USER_ERROR– User-generated fatal errorsE_COMPILE_ERROR– Fatal compile-time errorsE_RECOVERABLE_ERROR– Catchable fatal errors
Recovery Mode
Since WordPress 5.2.0, fatal errors trigger Recovery Mode:
- Error detected and logged
- Recovery email sent to admin
- Problematic plugin/theme paused
- Site remains accessible via recovery link
- Admin can fix issue and resume normal operation
Best Practices
Creating WP_Error
// Simple error
return new WP_Error( 'invalid_id', 'Invalid post ID.' );
// With data (include HTTP status for REST API)
return new WP_Error(
'rest_forbidden',
'You do not have permission.',
array( 'status' => 403 )
);
// Multiple errors
$error = new WP_Error();
$error->add( 'field_email', 'Invalid email address.' );
$error->add( 'field_name', 'Name is required.' );
return $error;
Checking Errors
// Always check return values
$result = wp_insert_post( $args );
if ( is_wp_error( $result ) ) {
error_log( $result->get_error_message() );
return $result; // Propagate error
}
Using wp_die()
// Simple die
wp_die( 'Access denied.' );
// With WP_Error
wp_die( new WP_Error( 'forbidden', 'Access denied.', array( 'status' => 403 ) ) );
// With options
wp_die( 'Error message', 'Error Title', array(
'response' => 500,
'back_link' => true,
) );