Dead Letter Exchanges

Messages from a queue can be 'dead-lettered'; that is, republished to another exchange when any of the following events occur:

  • The message is rejected (basic.reject or basic.nack) with requeue=false,
  • The TTL for the message expires; or
  • The queue length limit is exceeded.

Dead letter exchanges (DLXs) are normal exchanges. They can be any of the usual types and are declared as usual.

For any given queue, a DLX can be defined by clients using the queue's arguments, or in the server using policies. In the case where both policy and arguments specify a DLX, the one specified in arguments overrules the one specified in policy.

Configuration using arguments

To set the dead letter exchange for a queue, set the x-dead-letter-exchange argument to the name of the exchange:

channel.exchangeDeclare("", "direct");

Map<String, Object> args = new HashMap<String, Object>();
args.put("x-dead-letter-exchange", "");
channel.queueDeclare("myqueue", false, false, false, args);

The code above declares a new exchange called and sets this new exchange as the dead letter exchange for a newly created queue. Note that the exchange does not have to be declared when the queue is declared, but it should exist by the time messages need to be dead-lettered; if it is missing then, the messages will be silently dropped.

You may also specify a routing key to be used when dead-lettering messages. If this is not set, the message's own routing keys will be used.

args.put("x-dead-letter-routing-key", "some-routing-key");

When a dead letter exchange has been specified, in addition to the usual configure permissions on the declared queue, the user needs to have read permissions on that queue and write permissions on the dead letter exchange. Permissions are verified at the time of queue declaration.

Configuration using policy

To specify a DLX using policy, add the key "dead-letter-exchange" to a policy definition. For example:

rabbitmqctl set_policy DLX ".*" '{"dead-letter-exchange":"my-dlx"}' --apply-to queues
rabbitmqctl (Windows)
rabbitmqctl set_policy DLX ".*" "{""dead-letter-exchange"":""my-dlx""}" --apply-to queues

This applies the DLX "my-dlx" to all queues.

Similarly, an explicit routing key can be specified by adding the key "dead-letter-routing-key" to the policy.

Policies can also be defined using the management plugin, see the policy documentation for more details.

Routing Dead-Lettered Messages

Dead-lettered messages are routed to their dead letter exchange either:

  • with the routing key specified for the queue they were on; or, if this was not set,
  • with the same routing keys they were originally published with.

For example, if you publish a message to an exchange with routing key foo, and that message is dead-lettered, it will be published to its dead letter exchange with routing key foo. If the queue the message originally landed on had been declared with x-dead-letter-routing-key set to bar, then the message will be published to its dead letter exchange with routing key bar.

Note that, if a specific routing key was not set for the queue, messages on it are dead-lettered with all their original routing keys. This includes routing keys added by the CC and BCC headers (see Sender-selected distribution for details on these two headers).

Dead-lettered messages are re-published with publisher confirms turned on internally so, the "dead-letter queues" (DLX routing targets) the messages eventually land on must confirm the messages before they are removed from the original queue. In other words, the "publishing" (the one in which messages expired) queue will not remove messages before the dead-letter queues acknowledge receiving them (see Confirms for details on the guarantees made). Note that, in the event of an unclean broker shutdown, the same message may be duplicated on both the original queue and on the dead-lettering destination queues.

It is possible to form a cycle of message dead-lettering. For instance, this can happen when a queue dead-letters messages to the default exchange without specifiying a dead-letter routing key. Messages in such cycles (i.e. messages that reach the same queue twice) will be dropped if there was no rejections in the entire cycle.

Dead-Lettered Messages

Dead-lettering a message modifies its headers:

  • the exchange name is replaced with that of the latest dead-letter exchange,
  • the routing key may be replaced with that specified in a queue performing dead lettering,
  • if the above happens, the CC header will also be removed, and
  • the BCC header will be removed as per Sender-selected distribution.

The dead-lettering process adds an array to the header of each dead-lettered message named x-death. This array contains an entry for each dead lettering event, identified by a pair of {queue, reason}. Each such entry is a table that consists of several fields:

  • queue - the name of the queue the message was in before it was dead-lettered,
  • reason - see below,
  • time - the date and time the message was dead lettered as a 64-bit AMQP format timestamp,
  • exchange - the exchange the message was published to (note that this will be a dead letter exchange if the message is dead lettered multiple times),
  • routing-keys - the routing keys (including CC keys but excluding BCC ones) the message was published with,
  • count - how many times this message was dead-lettered in this queue for this reason, and
  • original-expiration (if the message was dead-letterered due to per-message TTL) - the original expiration property of the message. The expiration property is removed from the message on dead-lettering in order to prevent it from expiring again in any queues it is routed to.
New entries are prepended to the beginning of the x-death array. In case x-death already contains an entry with the same queue and dead lettering reason, its count field will be incremented and it will be moved to the beginning of the array.

The reason is a name describing why the message was dead-lettered and is one of the following:

  • rejected - the message was rejected with requeue=false,
  • expired - the TTL of the message expired; or
  • maxlen - the maximum allowed queue length was exceeded.

Note that the array is sorted most-recent-first, so the most recent dead-lettering will be recorded in the first entry.