Skip to content

Best tools to manage a dead letter queue in Kafka

Comparisons
Chad Harris·September 22, 2026·11 min read

Kafka dead letter queue tools cover two jobs: writing failed records to the DLQ (Kafka Connect’s errors.deadletterqueue settings, Spring Kafka’s DeadLetterPublishingRecoverer, Kafka Streams exception handlers), and working the DLQ afterwards (the Kafka CLI and kcat, a custom replay consumer, and UIs such as Kpow, AKHQ, Kafbat UI and Conduktor Console).

Creating a DLQ is the well-documented half. Inspecting, triaging, repairing and replaying what lands in it is where teams end up writing scripts at 2am, so that half gets most of the scoring here. Kpow is Factor House’s product, and I work at Factor House as a Solutions Architect, so it is scored on the same rubric and the same sources as every other option. For the wider tooling picture, see the complete Kafka guide.

What to score a DLQ tool on

A dead letter queue is a topic that holds records a consumer could not process, so the main partition can keep moving. Kafka has no DLQ at the broker level outside Kafka Connect, so every option here is either a framework that writes to one or a tool that reads from one. Five criteria separate them.

1. Does every DLQ record carry its error context?

A DLQ record without the reason it failed, and without the topic, partition and offset it came from, is a record you have to reverse-engineer. A failure reason in a header, alongside a retry count, fixes that, and the frameworks differ on whether they write it for you. Kafka Connect only adds its error headers when errors.deadletterqueue.context.headers.enable is true, and it defaults to false. Spring Kafka’s recoverer adds exception information to standard DLT headers.

2. Can you triage it without writing a script?

The baseline for inspecting a DLQ is CLI scripts against the topic. That is fine for five records and miserable for five thousand. A good tool lets you browse, search and filter the DLQ by header, key or payload field, so you can group failures by cause before you decide what to do with them. Search across topics is its own tooling category, compared in the best tools to search messages across Kafka topics. The steady-state goal for a DLQ is effectively zero messages, because every entry is a signal that something failed and you now pay an operational tax to unwind it. Triage is how you find the underlying fault and stop paying.

3. Can you repair a record, and is the original kept intact?

Some records only need replaying once a downstream fault is fixed. Others need a field corrected first. Those are different operations, and a tool should keep them separate. A byte-for-byte replay leaves the record exactly as the producing service wrote it. That matters in a SOC 2 audit, where the question is whether any other person or process could have altered data on the topic. A repaired record is a new record, and it should be produced as one, with a record of who changed what.

4. Does replay reach only the consumer that failed, and only once?

The simplest replay puts the record back on the main topic. That works at low scale with a single consumer. With several consumer groups on the topic, every one of them processes the replayed record again, and a service that is not idempotent sends a second email or places a second order. I have seen several multi-million dollar incidents caused by exactly that. The pattern that works well is a retry topic per topic and consumer pair, with the DLQ as the needs-manual-intervention path, so replay is targeted. The tool then has to track what it has already re-driven, and carry a retry count so a record that fails again cannot loop between the DLQ and the retry topic forever.

5. Who can read the DLQ, and who can replay it?

A DLQ holds the payloads your consumers choked on, which often means the unusual ones: the malformed customer record, the field that should have been masked. Reading it and writing from it both deserve access control, and replay deserves an audit trail. The same SOC 2 argument applies here: if anyone can produce to a retry topic, the retry topic is a path for altered data into your pipeline.

Dead letter queue tools compared

The first three rows write to a DLQ. The rest work one after it fills. Choosing where to put the DLQ in your design, rather than which tool manages it, is covered in dead letter queues in Kafka: patterns and pitfalls, which compares the Spring Kafka, Kafka Connect and Kafka Streams implementations with code.

Tool Job Error context on the record Triage Repair Replay Access control and audit Source
Kafka Connect errors.tolerance + DLQ Writes failed records from sink connectors to a DLQ topic Error headers, only when errors.deadletterqueue.context.headers.enable=true None, it only writes None None Whoever edits the connector config Apache Kafka docs
Spring Kafka DeadLetterPublishingRecoverer Writes failed records to <topic>-dlt, same partition by default Exception class, message and stack trace in standard DLT headers None, it only writes None @RetryableTopic adds retry topics before the DLT Whoever deploys the application Spring Kafka docs
Kafka Streams exception handlers Log-and-continue, log-and-fail, or a custom handler that writes to a quarantine topic Whatever your custom handler adds None None None Whoever deploys the application Apache Kafka docs
kafka-console-consumer.sh + kafka-console-producer.sh Read and re-produce records by hand Prints headers with print.headers=true Scroll and grep Edit text, then re-produce Manual, with no tracking Whoever holds CLI credentials, no audit Apache Kafka docs
kcat Scriptable read and produce Prints headers with a format string Pipe to jq or grep In your script Scriptable, with no tracking Whoever holds credentials, no audit GitHub: edenhill/kcat
Custom DLQ replay consumer A dedicated consumer that replays from the DLQ to a retry topic Reads the headers you wrote None beyond logs In code Targeted, with its committed offset tracking what was replayed Whatever your deployment controls allow Consumer-side DLQ pattern
Kpow Inspect, clone and re-produce DLQ records Shows headers with a headers deserializer, plus schema ID metadata Data Inspect search with kJQ filters across topics, long-lived result tabs Send records to Data Produce, amend, produce Clone to topic, byte for byte, for one record or a whole result set RBAC per clone source and sink, bulk clone behind its own permission, audit log Kpow docs
AKHQ Browse and produce Record view in the UI, so it shows what the writer put there Search and live tail in the UI Produce an edited record Manual produce Roles for topic data, opt-in audit events for produce and delete akhq.io
Kafbat UI Browse and produce Message view in the UI, so it shows what the writer put there Message browser with live view and CEL message filters Produce an edited message Manual produce RBAC with separate read, produce and delete permissions, audit log to a topic or console Kafbat UI docs
Conduktor Console Browse, consume and produce Record view in the UI, so it shows what the writer put there Browse and consume in the UI Produce an edited record Manual produce RBAC and an audit log Conduktor’s documentation, Topics, RBAC and Audit logs pages

How the options score

On error context, Spring Kafka is the best writer out of the box, because its recoverer adds exception headers without extra configuration. Kafka Connect matches it once you enable the context headers. Kafka Streams leaves it to your handler.

On triage, the UIs win over scripts, and Kpow leads among them because its search runs across several topics at once with server-side kJQ filters, and its result tabs keep their cursor so you can continue consuming a DLQ over days. AKHQ, Kafbat UI and Conduktor all make browsing a DLQ far faster than the CLI.

On repair versus byte-exact replay, Kpow separates the two as distinct actions: Clone to topic for an untouched copy, Data Produce for an amended record. With the CLI, kcat and the other UIs, replay means producing a record again, which is where headers, keys and serialization drift in.

On targeted, tracked replay, a custom replay consumer is still the most precise answer, because its committed offset records exactly what has been re-driven and it can increment a retry count header in code. Kpow’s clone RBAC can restrict a DLQ’s records to its own retry topic, which enforces the targeting. Neither the CLI nor the general-purpose UIs track what was replayed.

On access and audit, Kpow gives the most granular control: separate permissions for which topics may be a clone source and which a clone sink, a separate permission for bulk clones, and every clone in the audit log. Kafbat UI’s split between read, produce and delete permissions and its audit topic is the strongest open-source option.

Each option in detail

Rank 1

43 out of 50 Total

Listed first because it is our product. Scores are unadjusted.

Type
Kafka UI
Bulk clone
Enterprise
Error context on the record
7 out of 10
Triage without a script
10 out of 10
Repair, original kept intact
10 out of 10
Targeted, tracked replay
6 out of 10
Access control and audit
10 out of 10

What it does. Data Inspect searches the DLQ, Clone to topic copies records byte for byte to a permitted topic, Data Produce re-produces amended records, and RBAC and the audit log apply to all of it.

Where it wins. Triage across topics and the separation of untouched replay from repaired replay, with the permissions to enforce where replays may go.

Where it falls short. It does not track which records have already been replayed or maintain a retry count for you, so the retry-count discipline still belongs in your consumer. Bulk clone is an Enterprise feature.

Rank 2

AKHQ and Kafbat UI

akhq.io, github.com/kafbat/kafka-ui

26 out of 50 Total

Type
Open-source Kafka UIs
Error context on the record
6 out of 10
Triage without a script
7 out of 10
Repair, original kept intact
4 out of 10
Targeted, tracked replay
2 out of 10
Access control and audit
7 out of 10

What they do. Open-source UIs that browse a DLQ topic and produce records back to a topic.

Where they win. Free, self-hosted, and much faster than the CLI for reading a DLQ.

Where they fall short. Replay is a manual produce, one record at a time, with nothing tracking what has been replayed.

Rank 3

Conduktor Console

conduktor.io

26 out of 50 Total

Type
Commercial console
Error context on the record
6 out of 10
Triage without a script
6 out of 10
Repair, original kept intact
4 out of 10
Targeted, tracked replay
2 out of 10
Access control and audit
8 out of 10

What it does. A commercial console that browses, consumes and produces topic data, with RBAC and an audit log.

Where it wins. A polished browsing and produce flow for teams already running it.

Where it falls short. Replay is still a produce operation rather than a byte-level copy.

Rank 4

A custom DLQ replay consumer

23 out of 50 Total

Type
Consumer you build
Replay
To a retry topic, offset-tracked
Error context on the record
5 out of 10
Triage without a script
1 out of 10
Repair, original kept intact
5 out of 10
Targeted, tracked replay
10 out of 10
Access control and audit
2 out of 10

What it does. A separate consumer on the DLQ that replays one record or many onto the retry topic, commits its own offset to track what it has replayed, and increments a retry count header.

Where it wins. Exact control over targeting, ordering and retry limits. The pattern is written up in dead letter queues in Kafka: a consumer-side, Kafka-only approach.

Where it falls short. You build and maintain it, and it gives you no way to look inside the DLQ before you replay.

Rank 5

Spring Kafka DeadLetterPublishingRecoverer

docs.spring.io

15 out of 50 Total

Type
DLQ writer
Writes to
<topic>-dlt
Retries
@RetryableTopic retry topics
Error context on the record
10 out of 10
Triage without a script
0 out of 10
Repair, original kept intact
0 out of 10
Targeted, tracked replay
3 out of 10
Access control and audit
2 out of 10

What it does. Publishes a record that has exhausted its retries, or failed deserialization via the ErrorHandlingDeserializer, to <topic>-dlt.

Where it wins. Error headers by default, and @RetryableTopic adds non-blocking retry topics in front of the DLT.

Where it falls short. It writes to the DLQ and stops there. The default resolver also needs the DLT to have at least as many partitions as the source topic.

Rank 6

Kafka CLI and kcat

kafka.apache.org, github.com/edenhill/kcat

14 out of 50 Total

Type
Command-line tools
Replay
Manual or scripted produce
Error context on the record
5 out of 10
Triage without a script
3 out of 10
Repair, original kept intact
3 out of 10
Targeted, tracked replay
2 out of 10
Access control and audit
1 out of 10

What they do. Read a DLQ with headers printed, and re-produce records by hand or from a script.

Where they win. Always available, and kcat is easy to script into a one-off triage pipeline with jq.

Where they fall short. No tracking of what was replayed, no audit, and every replay is a re-produce, so keys, headers and serialization have to be carried across by hand.

Rank 7

Kafka Connect dead letter queue

kafka.apache.org

9 out of 50 Total

Type
DLQ writer
Scope
Sink connectors only
Setup
Configuration only
Error context on the record
7 out of 10
Triage without a script
0 out of 10
Repair, original kept intact
0 out of 10
Targeted, tracked replay
0 out of 10
Access control and audit
2 out of 10

What it does. With errors.tolerance=all a sink connector skips records that fail conversion or transformation, and errors.deadletterqueue.topic.name writes them to a DLQ topic.

Where it wins. Configuration only, with no code.

Where it falls short. Sink connectors only, converter and transform errors only, and the error headers are off by default.

Rank 8

Kafka Streams exception handlers

kafka.apache.org

6 out of 50 Total

Type
DLQ writer
DLQ
Custom handler you write
Error context on the record
4 out of 10
Triage without a script
0 out of 10
Repair, original kept intact
0 out of 10
Targeted, tracked replay
0 out of 10
Access control and audit
2 out of 10

What it does. Chooses whether a record that fails to deserialize, process or produce stops the application or is logged and skipped.

Where it wins. Built in, with no extra infrastructure.

Where it falls short. A DLQ means a custom handler, and the Apache documentation notes those writes sit outside Streams’ processing guarantees.

How Factor House approaches DLQs

Kpow treats the DLQ as data to investigate first and replay second. Kpow’s Data Inspect searches one or many topics with kJQ filters compiled and executed on the server, and a result tab keeps its cursor, so you can keep consuming new DLQ records into the same view. Clone to topic makes a byte-level copy of one record, or a whole result set through bulk actions, into a topic you choose. RBAC actions TOPIC_CLONE_SOURCE and TOPIC_CLONE_SINK restrict which topics can be cloned from and to, for example allowing records from *.dlq topics to go only to *.retry topics. Records that need a fix go to Data Produce instead, where you amend them before producing. Every clone and produce lands in the audit log. In the live Kpow demo you can run a Data Inspect query across several topics with a kJQ filter, which is the same search you would use to group a DLQ’s records by cause. Clone to topic for DLQs walks through the workflow, and the Kpow product page covers the rest of the product.

If the records in your DLQ are there because of one malformed record stalling a consumer, the upstream problem is a poison pill, and how to find and skip a poison pill covers clearing it. When the DLQ fills with records that fail to decode, how to diagnose a Kafka deserialization error covers finding the cause. The Kafka Connect guide covers where Connect’s error handling sits in a pipeline, and Kafka with Spring Boot covers the listener side.

Kpow live demo

Inspect and replay DLQ records live

Open the Kpow demo to search a topic by header or payload, decode the failed records and see how a replay would be scoped.

Built for platform and data engineers running Kafka in production.

Try the Kpow demo

FAQ

What is the best tool to manage a Kafka dead letter queue?

Pick a writer for each application type (Kafka Connect’s DLQ settings, Spring Kafka’s DeadLetterPublishingRecoverer, or a Kafka Streams handler), then a tool to work the DLQ. For triage and governed replay, Kpow’s Data Inspect and Clone to topic cover the most ground. For exact, tracked replay to one consumer, a small custom replay consumer is still the most precise option.

Does Kafka have a built-in dead letter queue?

Only Kafka Connect, and only for sink connectors. Everywhere else a DLQ is a topic your consumer or framework writes to.

How do I replay messages from a Kafka DLQ?

Fix the underlying fault first. Then replay each record to a retry topic that only the failing consumer reads, track what you have replayed, and carry a retry count header so a record that fails again goes back to the DLQ instead of looping. Replaying onto the main topic makes every consumer group process the record again.

Should DLQ records be edited before replay?

Only when the record itself is wrong. Replay the rest byte for byte so the data on the topic stays as the producer wrote it, and produce corrected records as new records with a record of who changed them.

Related reading