Files
spring-messaging-demo/rabbitmq/docs/03-the-silent-drop.md
2026-08-29 10:30:21 +05:30

2.8 KiB

← Exchanges · Module README · Dead-lettering →

3. The message that goes nowhere and says nothing

Publish to orders.direct with routing key amend. No binding matches. The broker discards the message. convertAndSend returns normally. Nothing is logged, by the broker or by Spring, and the message is gone.

This is correct AMQP behaviour and it is the single most expensive default in RabbitMQ, because the symptom is "the consumer isn't running" and the cause is three services away.

Two settings are required, and they live in different places:

spring:
  rabbitmq:
    publisher-returns: true      # on the connection factory
    template:
      mandatory: true            # on the template

publisher-returns puts the connection into a mode where returns are listened for at all. mandatory asks the broker to return this publish if it is unroutable. Setting only mandatory — which is what most examples show — changes nothing, and the test in this module was written that way first and failed.

Then register a callback:

template.setReturnsCallback((returned) -> log.error("unroutable: exchange={} key={} {} {}",
        returned.getExchange(), returned.getRoutingKey(),
        returned.getReplyCode(), returned.getReplyText()));

From docs/output/unroutable.txt:

replyCode    312
replyText    NO_ROUTE
exchange     orders.direct
routingKey   amend

312 NO_ROUTE is the code worth putting in an alert.

What returns still do not give you

The callback fires asynchronously, on the connection's thread. convertAndSend had already returned successfully by then. So a return tells you a message was lost; it does not let you stop the caller from believing it succeeded.

If the caller must know, you need publisher confirms as well:

spring:
  rabbitmq:
    publisher-confirm-type: correlated

Confirms and returns answer different questions, and you usually want both:

answers
return the broker had nowhere to route this
confirm the broker took responsibility for this (ack), or refused it (nack)

A message that is unroutable gets a return followed by an ack — the broker successfully did nothing with it. So a confirm alone will not tell you the message vanished, which is the trap inside the trap.

The alternate exchange

The topology-level version of the same fix: give an exchange an alternate-exchange argument and unroutable messages go there instead of being dropped, with no publisher-side configuration at all. It costs one exchange and one queue, and it catches the publishes made by services that forgot to set mandatory.

Dead-lettering →