Skip to content
🎉 Launch sale:50% off everything over $22 — automatically applied at checkout· ends Aug 2Shop the sale →
DevOps AI ToolKit
Newsletter
All guides
AI for RabbitMQ By James Joyner IV · · 8 min read Last reviewed Jul 2026

RabbitMQ Error: Messages Disappearing Due to Message TTL Expiry

Quick answer

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.

Part of the RabbitMQ Cluster, Queue & Resource Errors hub
  • #rabbitmq
  • #messaging
  • #troubleshooting
  • #errors
Free toolkit

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-ttl set far lower than intended (milliseconds vs. seconds confusion).
  • A policy applying message-ttl to more queues than expected via a broad pattern.
  • Publishers setting the AMQP expiration property (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

  1. 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.

  1. 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
  1. Fix a mistakenly narrow policy pattern that matched more queues than intended:
rabbitmqctl clear_policy -p / broad-ttl
  1. Check the publisher’s expiration property. It is a string of milliseconds; "60000" means 60 seconds, not 60 minutes. Remove it if you did not intend a per-message TTL.

  2. 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
  1. 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.
  • 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-expires removing 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.

Free download · 368-page PDF

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?

Free download · 368-page PDF

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.