Spring AMQP
Ack/Nack
Messaging Protocols
Software Development
Application Programming Interface

How to use Ack or Nack in Spring AMQP

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In Spring AMQP, acknowledgments decide what RabbitMQ should do with a message after delivery. An ack tells the broker processing succeeded and the message can be removed, while a nack tells the broker processing failed and the message should be requeued or discarded based on your choice.

Understand the Acknowledge Modes

Spring listener containers usually run in one of three modes:

  • 'AUTO: Spring acknowledges automatically when the listener completes successfully'
  • 'MANUAL: your code must acknowledge or reject the message'
  • 'NONE: RabbitMQ treats delivery as auto-ack and no acknowledgments are expected'

For most applications, AUTO is the default because it is simple. Choose MANUAL only when you need explicit control, such as conditional requeue, partial batch handling, or integration with custom retry rules.

Configure Manual Acknowledgment

To use ack and nack directly, configure the listener container for manual mode and receive the delivery tag in the listener method.

java
1import org.springframework.amqp.core.AcknowledgeMode;
2import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
3import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactoryConfigurer;
4import org.springframework.context.annotation.Bean;
5import org.springframework.context.annotation.Configuration;
6import org.springframework.amqp.rabbit.connection.ConnectionFactory;
7
8@Configuration
9public class RabbitConfig {
10    @Bean
11    SimpleRabbitListenerContainerFactory manualAckFactory(
12            SimpleRabbitListenerContainerFactoryConfigurer configurer,
13            ConnectionFactory connectionFactory) {
14        SimpleRabbitListenerContainerFactory factory =
15                new SimpleRabbitListenerContainerFactory();
16        configurer.configure(factory, connectionFactory);
17        factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
18        return factory;
19    }
20}

Then use Channel.basicAck or Channel.basicNack in the listener:

java
1import com.rabbitmq.client.Channel;
2import java.io.IOException;
3import org.springframework.amqp.rabbit.annotation.RabbitListener;
4import org.springframework.amqp.support.AmqpHeaders;
5import org.springframework.messaging.handler.annotation.Header;
6import org.springframework.stereotype.Component;
7
8@Component
9public class OrderListener {
10    @RabbitListener(queues = "orders.queue", containerFactory = "manualAckFactory")
11    public void handle(String payload,
12                       Channel channel,
13                       @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
14        try {
15            process(payload);
16            channel.basicAck(tag, false);
17        } catch (RetryableBusinessException ex) {
18            channel.basicNack(tag, false, true);
19        } catch (Exception ex) {
20            channel.basicNack(tag, false, false);
21        }
22    }
23
24    private void process(String payload) {
25        System.out.println(payload);
26    }
27}

The last boolean on basicNack matters. true requeues the message. false rejects it so it can be dead-lettered or dropped based on queue configuration.

When to Ack, Nack, or Reject

Use ack when processing is complete and durable enough that you do not want the message again. Use nack with requeue for failures that may succeed later, such as a temporary downstream outage. Use nack without requeue, or basicReject, for poison messages that will fail every time.

That decision should be based on failure type, not on a blanket rule. Requeuing everything often creates redelivery loops and queue churn, especially when the payload is permanently invalid.

If you do not need this level of control, keep AUTO mode and let Spring handle success and failure with the container's error strategy. Manual mode is powerful, but it also makes it easier to forget an acknowledgment path and leave messages unacked.

One good design pattern is to align acknowledgment decisions with your dead-letter setup. Temporary infrastructure failures can be requeued or retried, while invalid payloads should usually be rejected and routed to a dead-letter exchange for inspection instead of bouncing through the same queue forever.

Common Pitfalls

  • Turning on MANUAL mode and forgetting to send either ack or nack.
  • Requeueing every failure and creating an infinite redelivery loop.
  • Using manual acknowledgment when the default automatic mode would be simpler.
  • Holding on to the Channel outside the current listener invocation.
  • Confusing broker acknowledgment with application-level success semantics.

Summary

  • 'ack removes a successfully processed message from the queue.'
  • 'nack lets you choose whether to requeue or reject a failed message.'
  • Use MANUAL mode only when you need explicit acknowledgment control.
  • Requeue only for failures that are likely to succeed on a later attempt.
  • Keep acknowledgment logic aligned with your retry and dead-letter strategy.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.