RabbitMQ Error: Messages Disappearing Due to Message TTL Expiry
Fix RabbitMQ messages vanishing from a queue: diagnose per-message and per-queue x-message-ttl expiry, expired messages dropped or dead-lettered, with rabbitmqctl.
- #rabbitmq
- #messaging
- #troubleshooting
- #errors
Stuck on this RabbitMQ error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Exact Error Message
TTL expiry is not an error you catch on the client; messages simply leave the queue. The management/CLI stats show them counted as dropped or dead-lettered:
$ rabbitmqctl list_queues name messages messages_ready message_stats.drop_unroutable
Timeout: 60.0 seconds ...
name messages messages_ready
orders 0 0
With per-message TTL and no DLX, the queue drains itself. When a DLX is attached, the log shows expiry-driven dead-lettering:
[info] <0.988.0> Message dead-lettered from queue 'orders' with reason 'expired'
[info] <0.988.0> (per-message TTL) to exchange 'dlx.orders'.
What It Means
RabbitMQ can expire messages two ways: a per-queue TTL set with the x-message-ttl argument (or a policy message-ttl), and a per-message TTL set in the expiration property when publishing. When a message’s TTL elapses, RabbitMQ removes it from the queue. If a dead-letter exchange is configured, the message is dead-lettered with reason expired; if not, it is silently discarded.
This is working as designed, but it surprises teams who set a TTL for one queue and later reuse the argument, or who set a small expiration value in milliseconds and mistake it for seconds.
Common Causes
- A per-queue
x-message-ttlset far lower than intended (milliseconds vs. seconds confusion). - A policy applying
message-ttlto more queues than expected via a broad pattern. - Publishers setting the AMQP
expirationproperty (a string, in milliseconds) unintentionally. - Per-message TTL only expiring messages at the head of the queue, so consumers see uneven behaviour.
- A short TTL combined with a slow consumer, so messages expire before they are delivered.
Diagnostic Commands
Show the TTL arguments and effective policy on the queue:
rabbitmqctl list_queues name arguments policy --formatter=pretty_table
Inspect any policy applying message-ttl and how broadly it matches:
rabbitmqctl list_policies -p /
Watch the queue drain in real time to confirm messages leave without a consumer:
watch -n 2 'rabbitmqctl list_queues name messages messages_ready'
Check whether expired messages are being dead-lettered (reason expired):
rabbitmq-diagnostics log_tail --number 100
Step-by-Step Resolution
- Determine whether the TTL is per-queue or per-message. Read the queue arguments first:
rabbitmqctl list_queues name arguments -p /
If you see x-message-ttl, it is per-queue. If arguments are empty but messages still expire, a publisher is setting expiration.
- Correct a per-queue TTL. Queue arguments are immutable, so either apply the TTL via policy (mutable) or redeclare the queue with the right value. Set it with a policy in milliseconds:
rabbitmqctl set_policy -p / orders-ttl "^orders$" \
'{"message-ttl": 3600000}' --apply-to queues
- Fix a mistakenly narrow policy pattern that matched more queues than intended:
rabbitmqctl clear_policy -p / broad-ttl
-
Check the publisher’s
expirationproperty. It is a string of milliseconds;"60000"means 60 seconds, not 60 minutes. Remove it if you did not intend a per-message TTL. -
If expiry is legitimate but you are losing data, attach a dead-letter exchange so expired messages are captured instead of dropped, then reprocess them:
rabbitmqctl set_policy -p / orders-dlx "^orders$" \
'{"dead-letter-exchange": "dlx.orders"}' --apply-to queues
- Confirm the queue now retains messages for the expected window:
rabbitmqctl list_queues name messages messages_ready
Prevention
- Set TTLs via policy, not queue arguments, so you can adjust them without redeclaring queues.
- Always express TTL in milliseconds and document the unit next to the value.
- Attach a dead-letter exchange to any TTL’d queue so expiry is auditable, not silent loss.
- Keep policy patterns anchored (
^orders$) so a TTL never leaks onto unrelated queues. - Alert when a queue’s message count drops to zero without a corresponding delivery rate.
Related Errors
Message dead-lettered ... reason 'maxlen'— dropped for queue length, not TTL.Message dead-lettered ... reason 'rejected'— a consumer nacked with requeue false, not expiry.PRECONDITION_FAILED - inequivalent arg 'x-message-ttl'— redeclaring a queue with a different TTL argument.- Per-queue
x-expiresremoving the entire queue after inactivity, distinct from per-message TTL.
Frequently Asked Questions
Where did my messages go with no consumer running? A per-queue x-message-ttl or a publisher expiration property expired them. Without a DLX, expired messages are dropped silently.
Is the expiration property in seconds or milliseconds? Milliseconds, as a string. "5000" is five seconds, which is a common cause of messages vanishing far faster than expected.
Why do only some messages expire when I set per-message TTL? Per-message TTL is evaluated at the head of the queue, so a message behind a longer-lived one may not be discarded until it reaches the front. A TTL-audit prompt is in the RabbitMQ prompt library.
How do I stop losing expired messages entirely? Attach a dead-letter exchange so expired messages are re-routed with reason expired and can be reprocessed. For more messaging fixes, see the RabbitMQ guides.
Fixed it? Get 500 RabbitMQ & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Did this fix your issue?
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.