// problem · 24 july 2026 · 1 min
RabbitMQ redelivered a message I thought I had handled
Ack timing, not Laravel tries, decided the retry.
Time wasted: 2 hours
Problem
A consumer performed a side effect (an HTTP call) and then crashed. The broker redelivered. The side effect happened twice.
Environment
RabbitMQ
PHP consumer
Symptoms
Duplicate downstream requests. Consumer logs showed the handler starting twice for the same payload. I had not configured Laravel-style tries.
Expected
One downstream request per logical message.
Investigation
Looked at ack timing. Asked whether the handler was idempotent. Checked dead-letter configuration (there wasn’t a useful one).
Things I tried
Catching the exception and not rethrowing - which acked a failure. Prefetch tweaks. Neither fixed duplicates.
Root cause
The message was acknowledged after the side effect in the happy path, but a crash before ack meant RabbitMQ correctly redelivered. The handler was not idempotent.
Solution
Made the handler idempotent using a processed-message key. Ack after the side effect is recorded as complete. Failures go to a dead-letter queue instead of infinite requeue.
Why it worked
Redelivery is a broker feature. Idempotency is your feature. They are not Laravel retry counts.
Lesson
If the side effect is not idempotent, you do not have a retry strategy. You have a duplicate-risk strategy.
Found something wrong?
These notes can be incomplete, or wrong in a different environment. Suggest a correction on GitHub or email me at me@t4mer.net.
// related
// article
Understanding RabbitMQ after years of using Laravel queues
What changes when the queue is no longer an implementation detail of the framework.
// til
TIL: what idempotent actually means
Doing it twice should not change the outcome. Retries require that property or they become duplicate side effects.
// problem
Different PHP versions between CLI and the web server
The command line told the truth about a different binary than PHP-FPM.