Every failure is a Maildeno\MaildenoError, which extends PHP’s built-in \Exception.
use Maildeno\MaildenoError;
try {
$client->renderHtml($id);
} catch (MaildenoError $e) {
$e->code; // 'INVALID_API_KEY' | 'FORBIDDEN' | 'TEMPLATE_NOT_FOUND'
// | 'RENDER_ERROR' | 'NETWORK_ERROR' | 'TIMEOUT' | 'UNKNOWN'
$e->status; // HTTP status (0 for network/timeout)
$e->issues; // array of validation issues for 422s, else null
$e->getMessage(); // human-readable message, inherited from Exception
}
PHP’s \Exception already declares an int $code property. MaildenoError::$code widens that to hold the string SDK code shown above instead of an int. It’s public but should be treated as immutable — don’t assign to it.
|
Status mapping
| HTTP status | Code | Notes |
|---|---|---|
|
|
Key is missing, malformed, revoked, or expired. |
|
|
Key’s plan/scope doesn’t include the requested target. A message like "This API key does not have access to the 'mjml' target" means check your account/plan — it isn’t an SDK bug. |
|
|
|
|
|
Request body is invalid, or the render pipeline failed. |
other non-2xx |
|
— |
network failure |
|
— |
request exceeded timeout |
|
— |
|
This differs slightly from the JS/Python SDKs, which fall back to |
Checking $e→issues (validation errors)
When code is RENDER_ERROR and the cause is invalid input, issues gives field-level detail:
use Maildeno\MaildenoError;
try {
$client->renderHtml('not-a-uuid');
} catch (MaildenoError $e) {
if ($e->issues !== null) {
foreach ($e->issues as $issue) {
error_log(implode('.', $issue['loc']) . ' — ' . $issue['msg']);
}
}
}
// body.template_id — Input should be a valid UUID, ...
See also
-
Error Codes — the full cross-SDK code reference.
-
Caching — what happens to a render when the API is unreachable but a stale cached copy exists (spoiler: no exception).