Home/Learn/RabbitMQ/Consumer Acknowledgements (ack/nack)

Consumer Acknowledgements (ack/nack)

Intermediate
Reliability

basicAck signals successful processing; basicNack/basicReject requeues or discards the message. Unacknowledged messages are redelivered if the consumer disconnects.

Overview

Consumer acknowledgements are the mechanism RabbitMQ uses to know whether a message was processed successfully. Until a consumer acknowledges (or rejects) a message, RabbitMQ keeps the message in an "unacknowledged" state and holds it in memory. If the consumer connection drops without an ack, RabbitMQ requeues the message automatically for another consumer — at-least-once delivery. Three ack methods exist: basicAck (success, message deleted), basicNack (failure, optional requeue), and basicReject (single-message reject, optional requeue). The acknowledge mode can be automatic (broker deletes on delivery — fire-and-forget) or manual (safest). Combined with a prefetch count, manual acking is the foundation of reliable message processing in RabbitMQ.

basicAck, basicNack, basicReject — When to Use Each

Three ack primitives:

**basicAck(deliveryTag, multiple)** — acknowledge one or all unacked messages up to deliveryTag. Use after successful processing.

**basicNack(deliveryTag, multiple, requeue)** — negative-ack; requeue=true puts the message back at the front of the queue for retry; requeue=false routes it to the DLX (if configured) or discards it.

**basicReject(deliveryTag, requeue)** — same as basicNack but single-message only (no multiple flag).

Critical rule: a poisoned message that always fails processing will loop forever if you requeue=true. Route it to a DLQ after a max-retry count.

Java — AMQP Manual Ack
Channel channel = connection.createChannel();

// Set prefetch — limits unacked messages to 1 (fair dispatch)
channel.basicQos(1);

// Consume with manual ack (autoAck = false)
channel.basicConsume("order-queue", false, (consumerTag, delivery) -> {
    long deliveryTag = delivery.getEnvelope().getDeliveryTag();
    try {
        Order order = deserialize(delivery.getBody());
        orderService.process(order);

        // SUCCESS — ack the message; broker removes it
        channel.basicAck(deliveryTag, false);

    } catch (TransientException e) {
        // Transient failure — requeue for retry
        channel.basicNack(deliveryTag, false, true);

    } catch (PoisonMessageException e) {
        // Permanent failure — do NOT requeue; routes to DLX if configured
        channel.basicNack(deliveryTag, false, false);
    }
}, consumerTag -> {});

Auto-Ack vs Manual-Ack

Auto-ack (autoAck=true in basicConsume): the broker removes the message from the queue the moment it is delivered to the consumer — before the consumer processes it. If the consumer crashes mid-processing, the message is lost permanently. Use only for non-critical, idempotent, or fire-and-forget workloads.

Manual-ack (autoAck=false): the consumer explicitly calls basicAck or basicNack after processing. If the connection drops before acking, RabbitMQ requeues the message. This is the safe default for all production workloads. Pair with prefetch=1 for fair dispatch — the broker only sends the next message when the current one is acked.

Java + YAML — Ack Modes
# Auto-ack — dangerous, message lost if consumer crashes
channel.basicConsume("queue", true, deliverCallback, cancelCallback);
# Message is deleted on delivery — no recovery if crash during processing

# Manual-ack — safe, message requeued on consumer crash
channel.basicConsume("queue", false, deliverCallback, cancelCallback);
# Message stays unacked until basicAck or basicNack is called

# application.yml — Spring AMQP manual ack
spring:
  rabbitmq:
    listener:
      simple:
        acknowledge-mode: MANUAL   # or AUTO (auto-ack) or NONE (no-ack)
        prefetch: 10               # 10 unacked messages max per consumer

Spring AMQP — @RabbitListener with Manual Ack

In Spring AMQP, set acknowledge-mode: MANUAL and inject Channel + @Header(AmqpHeaders.DELIVERY_TAG) into the listener to call ack/nack manually. A simpler approach is to use the Channel parameter directly.

Java — Spring AMQP @RabbitListener + Manual Ack
@Component
public class OrderConsumer {

    @RabbitListener(queues = "order-queue")
    public void onOrder(
            Order order,
            Channel channel,
            @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws IOException {

        try {
            orderService.process(order);
            channel.basicAck(deliveryTag, false);     // success

        } catch (TemporaryException e) {
            // Requeue — will be retried
            channel.basicNack(deliveryTag, false, true);

        } catch (Exception e) {
            // Dead-letter — do not requeue
            log.error("Unrecoverable error processing order", e);
            channel.basicNack(deliveryTag, false, false);
        }
    }
}

Key Points to Remember

  • 1Manual-ack (autoAck=false) is the safe default: messages are requeued if the consumer crashes before acking.
  • 2basicAck → processed successfully; basicNack(requeue=true) → retry; basicNack(requeue=false) → dead-letter or discard.
  • 3Never requeue=true for a message that always fails — it creates an infinite poison-message loop; route to DLQ instead.
  • 4Set prefetch (basicQos) together with manual ack for fair dispatch: each consumer only receives the next message after acking the previous one.
  • 5Auto-ack deletes the message on delivery — before processing — acceptable only for non-critical fire-and-forget scenarios.
  • 6In Spring AMQP, set acknowledge-mode: MANUAL and inject Channel into the @RabbitListener method to call ack/nack.

Interview Questions

Sign in to ask Aria
1

What is the difference between basicAck and basicNack in RabbitMQ?

EasyAmazon
2

What happens to an unacknowledged message when the consumer connection drops?

EasyFlipkart
3

Why is auto-ack dangerous for critical message processing?

MediumUber
4

A message is nacked with requeue=true. What happens if it always fails processing?

MediumLinkedIn
5

How would you implement a max-retry policy before routing a message to a DLQ in RabbitMQ?

HardNetflix

Ask Aria about Consumer Acknowledgements (ack/nack)

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…