[← Exchanges](02-exchanges.md) · [Module README](../README.md) · [Dead-lettering →](04-dead-lettering.md) # 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:** ```yaml 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: ```java template.setReturnsCallback((returned) -> log.error("unroutable: exchange={} key={} {} {}", returned.getExchange(), returned.getRoutingKey(), returned.getReplyCode(), returned.getReplyText())); ``` From [`docs/output/unroutable.txt`](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: ```yaml 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 →](04-dead-lettering.md)