Retries & dead letters
Whether a failed event gets retried or dead-lettered comes down to which exception your handler raises.
The two exceptions
from pymidil.event.exceptions import RetryableEventError, NonRetryableEventError
class OrderSubscriber(EventSubscriber):
async def handle(self, event) -> None:
if upstream_busy():
raise RetryableEventError("upstream throttled") # → redelivered with backoff
if event.body.get("amount", 0) < 0:
raise NonRetryableEventError("invalid amount") # → dead-lettered, no retry
await process(event)
RetryableEventError: the event is requeued and redelivered after a backoff delay.NonRetryableEventError(or any other unhandled exception): the event is moved straight to the dead-letter queue. Use this deliberately once you've decided a message will never succeed no matter how many times it's retried (bad data, a permanently missing reference, and so on).
If you have multiple subscribers on the same event and any one of them raises a retryable error, the whole delivery is retried, even if another subscriber succeeded or raised a non-retryable error.
Backoff
The built-in SQSConsumer retries with exponential backoff, configurable per consumer:
SQSConsumerEventConfig(
type="sqs",
queue_url="...",
dlq_url="...",
backoff_base_delay=5, # seconds before the first retry
backoff_max_delay=300, # backoff never grows past this
)
There's no built-in maximum attempt count. A RetryableEventError keeps getting redelivered
(with growing backoff) until it either succeeds or you decide it should stop and raise a
non-retryable error instead. If your queue has its own redrive policy configured on your broker,
that acts as an outer safety net independent of the SDK.
If you're observing an existing consumer
ConsumerObserver doesn't decide retry/dead-letter for you. That's already up to your existing
broker client and loop. What it needs from you is how to classify an outcome, so the right status
reaches the dashboard: report a failure as retryable or not based on whatever your own code already
knows (an exception type, an error code, a delivery-count header from your broker).
Where it shows up
Every attempt is visible in Event Detail's Retry Attempts tab, and anything currently dead-lettered is grouped by failure class in the DLQ Center.