Siren

Error Handling

How WP_Error and is_wp_error() translate to Siren's typed exception system.

Last updated: April 9, 2026

Error Handling

WordPress uses a dual-path error model: some functions return WP_Error objects on failure (which you check with is_wp_error()), while others return false or null. This means every call site needs defensive checks, and the type of error you get is inconsistent across the API.

Siren uses PHP exceptions. When something fails, it throws a typed exception. You catch what you need and let the rest propagate.

// WordPress: check for WP_Error after every operation
$result = wp_insert_post($args);

if (is_wp_error($result)) {
    echo $result->get_error_message();
    return;
}

// Some functions return false instead of WP_Error
$meta = get_post_meta($post_id, '_key', true);
if ($meta === false) {
    // Was it an error, or does the meta just not exist?
}
// Siren: catch typed exceptions
use PHPNomad\Datastore\Exceptions\RecordNotFoundException;
use PHPNomad\Datastore\Exceptions\DuplicateEntryException;

try {
    $program = $this->programs->getById($id);
} catch (RecordNotFoundException $e) {
    // The program does not exist — handle it
}

try {
    $this->collaborators->create($data);
} catch (DuplicateEntryException $e) {
    // A collaborator with this email already exists
}

Exception reference

Siren and PHPNomad use typed exceptions that tell you exactly what went wrong. Here are the most common ones you will encounter in extension development:

ExceptionWhen thrown
RecordNotFoundExceptiongetById() with a nonexistent ID, or a query that expects exactly one result finds none
DuplicateEntryExceptionCreating a record that violates a uniqueness constraint (e.g., duplicate email)
RestExceptionGeneric REST error that carries an HTTP status code and message
ValidationExceptionRequest validation fails, includes field-level error details
AuthorizationExceptionAuthenticated but insufficient permissions (HTTP 403)
AuthenticationExceptionNot authenticated at all (HTTP 401)

How does this work in REST controllers?

Siren’s REST controllers automatically catch these exceptions and return appropriate HTTP responses. You do not need to manually convert exceptions to REST error responses:

  • RecordNotFoundException returns a 404 response
  • ValidationException returns a 422 response with field-level errors
  • AuthorizationException returns a 403 response
  • AuthenticationException returns a 401 response
  • RestException returns whatever HTTP status code it carries

This means your controller methods can focus on the happy path. Throw an exception when something goes wrong, and the framework handles the REST error formatting.

What should I catch in extension code?

In extension code (listeners, transformers, services), catch the exceptions you can meaningfully handle and let the rest propagate. Common patterns:

// In a transformer: catch RecordNotFoundException when looking up mappings
try {
    $transaction = $this->transactions->getById($mappedId);
} catch (RecordNotFoundException $e) {
    // No Siren transaction for this order — return null to skip
    return null;
}

// In a listener: catch and log, but don't crash the event pipeline
try {
    $this->externalService->sync($data);
} catch (\Exception $e) {
    $this->logger->logException($e);
    // The sync failed, but the rest of the pipeline should continue
}

The transformer pattern is especially important. Transformers return null to skip events, so catching “record not found” and returning null is the standard way to handle cases where the data does not exist.

If you do not catch an exception in a listener, it will propagate up and may prevent subsequent listeners for the same event from executing. When in doubt, catch broadly in listeners and log the exception so you can diagnose issues without breaking the event pipeline.

Where to go next

For how REST controllers auto-catch exceptions and convert them to HTTP responses, see REST API Patterns. For the full REST error response format (including validation error details), see the Resource Reference Introduction.

For the full PHPNomad framework documentation, see PHPNomad.