-
Notifications
You must be signed in to change notification settings - Fork 0
Error Handling
The library raises typed exceptions so you can handle specific error conditions precisely.
PostmarkException # Base for all library exceptions
├── InvalidEmailException # Local Pydantic validation failure (before API call)
├── TimeoutException # Request timed out
└── PostmarkAPIException # Base for errors returned by the Postmark API
├── InvalidAPIKeyException # 401 – Invalid or missing API key
├── InactiveRecipientException # 406 – Recipient is inactive
├── ValidationException # 422 – Invalid request parameters
├── RateLimitException # 429 – Rate limit exceeded
└── ServerException # 500/503 – Postmark server error
All PostmarkAPIException subclasses carry:
-
message— Human-readable error description -
error_code— Postmark error code (from the response body) -
http_status— HTTP status code
The client automatically retries requests that fail with RateLimitException (429), ServerException (500/503), or TimeoutException. It uses exponential backoff with jitter and retries up to 3 times by default.
If the request still fails after all retries, the final exception is raised normally so you can handle it in your code.
To change the retry count, pass retries=N when constructing the client (see Getting Started).
import asyncio
import postmark
from postmark.exceptions import (
PostmarkAPIException,
InvalidAPIKeyException,
InactiveRecipientException,
ValidationException,
RateLimitException,
ServerException,
TimeoutException,
)
client = postmark.ServerClient("your-server-token")
async def main():
try:
response = await client.outbound.send({
"sender": "you@example.com",
"to": "recipient@example.com",
"subject": "Hello",
"text_body": "Hello!",
})
print(f"Sent: {response.message_id}")
except InvalidAPIKeyException as e:
print(f"Bad API key: {e.message}")
except InactiveRecipientException as e:
print(f"Inactive recipients: {e.inactive_recipients}")
except ValidationException as e:
print(f"Validation error [{e.error_code}]: {e.message}")
except RateLimitException:
print("Rate limited — slow down requests")
except ServerException as e:
print(f"Postmark server error (HTTP {e.http_status}): {e.message}")
except TimeoutException:
print("Request timed out")
except PostmarkAPIException as e:
print(f"Postmark API error [{e.error_code}]: {e.message}")
asyncio.run(main())InactiveRecipientException includes a parsed list of the inactive addresses.
from postmark.exceptions import InactiveRecipientException
try:
await client.outbound.send({...})
except InactiveRecipientException as e:
for addr in e.inactive_recipients:
print(f"Inactive: {addr}")InvalidEmailException is raised before any API call when the email data fails Pydantic model validation. It carries the raw Pydantic errors.
It can be imported from either the top-level postmark package or directly from postmark.exceptions:
# Top-level import (recommended)
from postmark import InvalidEmailException
# Or from the exceptions module
from postmark.exceptions import InvalidEmailException
try:
await client.outbound.send({
"sender": "not-an-email", # Invalid
"to": "recipient@example.com",
"subject": "Hello",
})
except InvalidEmailException as e:
print(e) # e.g. "Invalid email: sender: value is not a valid email address"PII warning:
InvalidEmailException(andvalidate_formatted_email) includes the invalid email address in the exception message and error details. If your application ships exceptions to a central log aggregator, ensure this aligns with your data-handling policy before logging these at a level that persists to external systems.
Use PostmarkException as a catch-all for any error raised by the library.
from postmark.exceptions import PostmarkException
try:
await client.outbound.send({...})
except PostmarkException as e:
print(f"Error: {e}")