Dramatiq retry_when for Sume: retry only retryable errors

A Dramatiq actor with retry_when that retries 429 and 5xx but not 402 or 400, and sends one stable Idempotency-Key so a retried Sume submit is not billed twice.

5 min readSume
All posts

In Dramatiq, give the Sume submit actor a retry_when predicate that returns true only for errors Sume marks retryable, and derive one Idempotency-Key from your own record so every attempt sends the same key. Dramatiq's reference describes retry_when as a callable taking the retry count and the exception, returning a bool, and says it takes precedence over max_retries when set (read 2026-10-04).

The retry decision comes from Sume's error docs: 429 and 503 are transient, while a 402 insufficient_credits or a 400 validation error will fail the same way every time.

What does the actor look like?

The exception carries the HTTP status and the retryable flag from the body, so the predicate stays a single line.

import os, dramatiq, requests

class SumeError(Exception):
    def __init__(self, status, retryable, code):
        super().__init__(f"{status} {code}")
        self.status, self.retryable, self.code = status, retryable, code

def should_retry(retries_so_far: int, exc: BaseException) -> bool:
    if isinstance(exc, SumeError):
        return exc.retryable or exc.status in (429, 503)
    return isinstance(exc, (requests.ConnectionError, requests.Timeout))

@dramatiq.actor(retry_when=should_retry, min_backoff=5_000, max_backoff=300_000)
def submit_hero(sku: str) -> None:
    r = requests.post("https://api.sume.com/v1/image-1.0/generate", timeout=30,
        headers={"x-api-key": os.environ["SUME_API_KEY"],
                 "Idempotency-Key": f"hero-{sku}-v1"},
        json={"prompt": f"Product hero shot, {sku}", "mode": "async"})
    if r.status_code >= 400:
        e = r.json().get("error", {})
        raise SumeError(r.status_code, bool(e.get("retryable")), e.get("code"))
    print(r.json()["data"]["request_id"])

Which errors deserve a retry?

Retry decision per Sume error, from Sume docs read 2026-10-04
Status and codeRetry?Why
429 rate_limitedYes, after retry-afterThe write budget refills; the job was not created
409 idempotency_key_in_useYes, after about a secondAnother request with the same key is in flight
503 studio_agent_upstream_unavailableYesA Sume-side outage; same key is safe
402 insufficient_creditsNoNeeds funding by a person
403 insufficient_scopeNoNeeds a different key
400 validation errorNoFix the input

Why does the key matter more than the backoff?

A retry after a timeout may follow a request Sume already accepted. With the same Idempotency-Key, Sume replays the original response instead of creating a second job, so you are not billed twice. A key built from uuid4() inside the actor would be new on every attempt and defeat that. Build it from the SKU, the order id or another stable value you own.

What else should the actor avoid?

  • Do not poll for the result inside the same actor; submit here and poll or take a webhook elsewhere.
  • Set max_backoff low enough that a retry lands within your own deadline.
  • Log the request_id from the error body on every failed attempt.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume