Retrying a failed job feels like the safe default. Sometimes it just delays a failure you already know is coming.
We ran into this while building the scheduler behind Cadencz. A batch of posts failed to publish, and the naive fix was to retry the whole batch. That turned one bad API key into hours of wasted retries across jobs that had nothing wrong with them.
The fix was splitting errors into two categories at the job level, not the batch level.
Permanent errors don't deserve a retry. A deleted social account. Revoked OAuth credentials. A malformed payload that will never parse. Retrying these wastes queue capacity and delays the alert a human actually needs to see.
Transient errors deserve a retry, with backoff. A timeout from the platform API. A rate limit response. A brief network blip. These resolve on their own, and exponential backoff stops you from hammering a service that's already struggling.
Why job-level matters more than batch-level: a batch of 50 scheduled posts might contain one job with a dead token and 49 jobs that are perfectly fine. Failing the whole batch on that one error either stalls 49 healthy jobs or forces a full retry that repeats the same dead job 50 times.
Tagging the error type at the point of failure, inside the job itself, lets the queue route each job correctly. Dead jobs go to a dead-letter queue for a human to fix. Transient jobs go back with backoff. Healthy jobs keep moving.
The practical rule: classify the error before you decide the retry policy. Not all failures are the same problem, so they shouldn't get the same fix.
What error taxonomy does your queue use, and where did the batch-level assumption break down for you?
Top comments (0)