Kafka Consumer
WakeupException Handling
Java
Software Development
Programming Errors

Kafka Consumer WakeupException Handling Java

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

WakeupException is not a normal business failure in a Kafka consumer. It is the standard mechanism for breaking a thread out of poll() so the consumer can shut down or change control flow cleanly.

The usual pattern is simple: one thread runs the poll loop, another thread calls consumer.wakeup(), and the poll loop catches WakeupException, checks whether shutdown was intended, and then closes the consumer in a finally block.

Understand What wakeup() Is For

Kafka consumer poll() can block for a while. If you need to stop the consumer promptly, you do not want to wait for the poll timeout every time. wakeup() interrupts the blocked poll by causing it to throw WakeupException.

That makes WakeupException a control signal rather than a sign that record processing itself went wrong.

The basic shutdown pattern looks like this:

java
1import org.apache.kafka.clients.consumer.ConsumerConfig;
2import org.apache.kafka.clients.consumer.ConsumerRecords;
3import org.apache.kafka.clients.consumer.KafkaConsumer;
4import org.apache.kafka.common.errors.WakeupException;
5
6import java.time.Duration;
7import java.util.Collections;
8import java.util.Properties;
9
10public class SafeConsumer implements Runnable {
11    private final KafkaConsumer<String, String> consumer;
12    private volatile boolean closing = false;
13
14    public SafeConsumer(Properties props, String topic) {
15        this.consumer = new KafkaConsumer<>(props);
16        this.consumer.subscribe(Collections.singletonList(topic));
17    }
18
19    @Override
20    public void run() {
21        try {
22            while (!closing) {
23                ConsumerRecords<String, String> records =
24                    consumer.poll(Duration.ofSeconds(1));
25                records.forEach(record ->
26                    System.out.println(record.key() + ":" + record.value()));
27            }
28        } catch (WakeupException e) {
29            if (!closing) {
30                throw e;
31            }
32        } finally {
33            consumer.close();
34        }
35    }
36
37    public void shutdown() {
38        closing = true;
39        consumer.wakeup();
40    }
41}

This is the canonical design because it keeps all real consumer work on one thread while still allowing another thread to interrupt the poll cleanly.

Handle Shutdown Separately from Real Errors

The catch block should stay small. If shutdown was intentional, swallow the exception and let the consumer close. If shutdown was not intentional, rethrow it.

That distinction matters because WakeupException should not be treated like deserialization errors, authorization failures, or application processing exceptions. It means "stop polling now," not "the records are bad."

If you commit offsets manually, do that according to your processing guarantees, not blindly because WakeupException occurred. The shutdown signal and offset policy are different concerns.

Keep the Threading Model Clean

Kafka consumers are not generally safe for arbitrary multi-threaded use. The usual rule is:

  • one thread owns the consumer and runs poll()
  • other threads may call wakeup() to interrupt it

That is why the shutdown method in the example only flips a flag and calls wakeup(). It does not start polling or committing from another thread.

This is also why WakeupException is preferred over trying to stop the thread by force. It works with the consumer API instead of against it.

Common Pitfalls

The biggest mistake is treating WakeupException as an application error and logging it as though the consumer had failed unexpectedly. In many systems it simply means shutdown was requested.

Another common issue is swallowing every WakeupException without checking whether shutdown was intentional. If wakeup() was called unexpectedly, the consumer may hide a real control-flow bug.

It is also easy to violate the consumer threading model by having several threads interact with the same consumer instance. wakeup() is the safe exception, not a license for general concurrent access.

Finally, do not forget the finally block. Resource cleanup belongs there so the consumer closes cleanly whether shutdown was planned or not.

Summary

  • 'WakeupException is Kafka's normal escape hatch for breaking out of poll().'
  • Use consumer.wakeup() from another thread to trigger a controlled shutdown.
  • Catch the exception, check whether shutdown was intended, and close the consumer in finally.
  • Keep normal consumer operations on one owning thread.
  • Treat WakeupException as a control signal, not as a record-processing failure.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

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

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.