Dead Letter Queues in Kafka Connect

Dead Letter Queues in Kafka Connect

What happens when one bad record threatens to stop a whole data pipeline? Learn how Kafka Connect sets troublesome records aside in a special topic, how to configure it, how to watch it, and how to fix and replay what landed there.

Imagine a factory that packs thousands of parcels every hour on a moving conveyor belt. Every parcel has a label. One day, a parcel arrives with a smudged label that nobody can read. The belt worker has three choices. She can stop the entire belt until someone fixes the label. She can throw the parcel in the bin and carry on, hoping it was not important. Or she can put the parcel in a special tray marked “Problems”, let the belt keep running, and let a specialist sort it out later.

The third choice is the one that keeps factories running and customers happy. In Kafka Connect, that special tray has a name: the dead letter queue, often shortened to DLQ. In Kafka it is not a separate product. It is simply an ordinary Kafka topic where Connect places records that it could not process.

This guide explains the whole idea from the ground up. We will start with what Kafka Connect is, then look at the ways records can go wrong, then see exactly how a dead letter queue catches them. Along the way you will learn the settings, the hidden details, and the habits that separate a calm production pipeline from a noisy one.

i
Quick answer

A dead letter queue in Kafka Connect is a Kafka topic that receives copies of records a sink connector failed to process, so the connector can keep running while people investigate the failed records later. It is turned on with a topic name setting, and it works only for sink connectors.

Why One Bad Record Can Hurt So Much

To see why dead letter queues exist, start with what goes wrong without one.

Kafka is patient, and that is the problem

A Kafka topic is a long notebook of records. A consumer reads it one line at a time, in order, and remembers its place using a number called an offset. If the consumer cannot understand line 5,000, it cannot simply jump to line 5,001 without making a decision. If it retries line 5,000 forever, it never moves on. Everything behind that line waits.

A record that a consumer can never process is often called a poison pill. It is poisonous because it does not only fail itself. It blocks all the healthy records queued behind it.

Everyday analogy

Think of a single-lane road with a broken-down truck. The truck is only one vehicle, but the hundreds of cars behind it cannot pass. A dead letter queue is like a tow lane that moves the truck aside so traffic flows again.

What makes a record bad?

Records fail for many ordinary reasons. Nobody has to do anything wrong on purpose.

Format

Wrong shape

A producer sends plain text to a topic that the connector expects to hold structured JSON.

Schema

Unknown schema

A record refers to a schema that cannot be found, or does not match what the reader expects.

Corruption

Broken bytes

The bytes are cut off or damaged, so they cannot be turned back into a proper record.

Logic

Failed transformation

A step that reshapes the record hits an empty value it did not expect.

Target

Destination refuses it

The database or storage system rejects the record because it breaks a rule, such as a value that is too long.

Config

Human mistake

Someone points the connector at the wrong topic, so every record looks bad.

Three ways to react

ReactionWhat happensGood sideRisk
Stop everythingThe connector task fails until a human fixes itNothing is lost or skipped quietlyThe whole pipeline stalls for one record
Skip and forgetThe bad record is dropped and processing continuesPipeline keeps flowingSilent data loss
Set aside in a DLQThe bad record is saved in another topic and processing continuesFlow continues and nothing is lostSomeone must watch and handle the DLQ
!
Watch out

A dead letter queue is not magic. It moves the problem from “the pipeline is stuck” to “someone must look at the tray”. A DLQ that nobody watches is just a slower way to lose data.

The Vocabulary You Need First

A short list of words makes the rest of this article easy.

Kafka Connect

Kafka Connect is a tool that comes with Apache Kafka. Its job is to move data between Kafka and other systems, without you writing custom programs. You describe what you want in a small configuration, and Connect does the moving, the retrying, and the tracking of progress.

Source and sink

A source connector brings data into Kafka from somewhere else, for example from a database. A sink connector pushes data out of Kafka into somewhere else, for example into cloud storage or a search engine. This difference matters a lot in this article, because the dead letter queue feature is built for sinks.

Everyday analogy

Think of Kafka as a large warehouse. A source connector is the truck that delivers goods into the warehouse. A sink connector is the truck that takes goods out and delivers them to shops.

Worker, connector, and task

  • A worker is a running program (a process) that hosts connectors. Several workers together form a Connect cluster.
  • A connector is the named job you configure, such as “copy orders into cloud storage”.
  • A task is one unit of work inside a connector. A connector can split its job into several tasks that run in parallel, often one per group of partitions.

Converter

Kafka stores records as raw bytes. A converter turns those bytes into a structured object that the connector understands, and the other way around. Common converters read JSON, Avro, or Protobuf. There is one converter for the record key, one for the value, and one for headers.

Single message transform

A single message transform, or SMT, is a small step that changes each record on its way through, such as renaming a field, dropping a field, or routing the record to a different topic. SMTs can be chained one after another.

Offset and commit

An offset is a record’s position number in its partition. Connect commits offsets, meaning it records “I have finished up to here”, so a restart continues from the right place.

WordSimple meaning
Sink connectorMoves data from Kafka to an outside system
Source connectorMoves data from an outside system into Kafka
ConverterTurns bytes into objects and back
SMTA small per-record change step
TaskOne parallel worker inside a connector
Dead letter queueA Kafka topic that holds records that could not be processed

The Big Picture: A Side Tray for Problem Records

Here is the whole idea in one picture and a few sentences.

Without a dead letter queue, a sink connector reads records from a topic and writes them to a destination. If a record fails, the connector has only two options: stop, or skip. With a dead letter queue, there is a third path. The failed record is copied into a separate topic, and the connector goes on to the next record.

flowchart LR
    T["Source topic: orders"] --> C["Sink connector task"]
    C -->|"good records"| D[("Destination: cloud storage or database")]
    C -->|"bad records"| Q["Dead letter queue topic: dlq-orders-sink"]
    Q --> H["Human or repair job investigates"]
    
FIG 1 — Good records flow on, bad records take the side path

The picture shows a source topic feeding a sink connector task. Healthy records go through to the destination. Records that fail are diverted into the dead letter queue topic. From there, a person or a repair job can look at them, fix the cause, and decide what to do next.

Sink only
DLQ works for sink connectors
1 Topic
An ordinary Kafka topic
Off
Disabled by default
Everyday analogy

A post office does not stop the whole sorting line because one envelope has no address. That envelope goes into a “Return or investigate” bin, and the rest of the mail keeps moving.

Three key facts to remember

  • It is opt-in. Nothing goes to a DLQ unless you name a topic in the configuration.
  • It only works when errors are tolerated. You must also tell Connect to keep going after errors, otherwise the task still fails on the first bad record.
  • It is just a topic. You can read it with any consumer, set retention on it, secure it, and monitor it like any other topic.

Real-world picture: loading orders into storage

A retail company copies its orders topic into cloud storage every few minutes for analytics. On a Tuesday, a new mobile app version starts sending a malformed order. Without a DLQ, the copy job stops, and analytics is stale until an engineer wakes up. With a DLQ, all good orders keep landing, and the single malformed order waits in a tray for someone to review.

Where Can a Record Fail Inside Connect?

A record passes through several stations. A problem at any one of them can trigger error handling.

The stations of a sink connector

flowchart LR
    K["Kafka consumer reads raw bytes"] --> HC["Header converter"]
    HC --> KC["Key converter"]
    KC --> VC["Value converter"]
    VC --> SMT["Transformations (SMTs)"]
    SMT --> PUT["Connector writes to destination"]
    
FIG 2 — The path of one record through a sink connector task

A sink task first reads raw bytes with a normal Kafka consumer. Next come the converters for headers, key, and value. Then any transformations run in order. Finally the connector’s own code writes the record to the destination. Errors can occur at any of these stations.

StationExample failureCovered by Connect’s error handling?
Consumer readNetwork trouble talking to the brokerNo, handled by the consumer’s own retry logic
Header, key, or value converterBytes are not valid JSON, or the schema is unknownYes
TransformationA step meets a null it did not expectYes
Connector write (task logic)Database rejects the rowOnly if the connector reports it, or throws a retriable error
!
Watch out

Many beginners assume the DLQ catches every error a connector can have. It does not. It covers converter and transformation problems automatically. Errors inside the connector’s own write logic reach the DLQ only when that connector is written to report them. We cover that in a later chapter.

Source connectors have stations too

A source connector reads from an outside system, builds a record, runs transformations, and then uses converters to turn the record into bytes for Kafka. Errors in the transformation and converter stations can be tolerated, but there is no built-in dead letter queue for sources. Later we look at the practical alternatives.

Everyday analogy

Think of an airport baggage system. Bags pass a scanner, then a sorter, then a loader. If the scanner cannot read a tag, the airport’s safety net puts the bag aside. But if the loader damages a suitcase at the end, that is not something the scanner’s safety net can catch. Each station needs its own care.

The Default: Fail Fast and Stop

Before dead letter queues, let us understand what Connect does out of the box.

The setting called errors.tolerance

Connect has a setting named errors.tolerance. It has two values.

  • none is the default. The first error that Connect cannot fix makes the task fail.
  • all tells Connect to tolerate errors: skip the bad record and carry on.

What “fail” looks like

1

A bad record arrives

A converter or a transformation raises an error on record number 5,000.

2

The task stops

The task moves into a FAILED state and stops consuming.

3

Lag builds up

New records keep arriving in the topic, but nothing reads them. The gap between the newest record and the connector’s position grows.

4

A human is needed

Someone must read the log, understand the cause, fix it, and restart the task.

5

The same record may fail again

After the restart, the connector resumes at the same offset, meets the same bad record, and fails again, unless the cause was truly fixed.

Everyday analogy

This is the cautious factory manager who presses the big red stop button whenever one parcel looks odd. Nothing wrong ever ships, but the belt sits idle until someone walks over to investigate.

When fail-fast is the right choice

Stopping is not always bad. It is the safest option when every record matters absolutely and you want a human to look at the very first problem. Examples include early testing, brand-new pipelines, and financial feeds where a skipped record would be a serious issue. Fail-fast also protects you from a wider mistake, such as a wrong topic name, where every record is bad and continuing would just fill a DLQ with junk.

Strengths of fail-fast

  • No record is skipped by accident
  • Problems are noticed immediately
  • Simple to reason about
  • Prevents a flood of junk from a wrong configuration

Weaknesses of fail-fast

  • One record halts the whole pipeline
  • Recovery needs a human, even at night
  • Lag and delay grow while it is stuck
  • Restarting alone does not fix a real bad record

Skip and Continue: Tolerating All Errors

Setting tolerance to “all” keeps the pipeline moving, but the details are important.

What changes

With errors.tolerance set to all, a record that fails in a converter or transformation is no longer a reason to stop. Connect marks it as skipped and moves on to the next record. The task stays healthy, and lag stays low.

The silent data loss trap

Here is the dangerous part. If you set tolerance to “all” and do nothing else, the bad records are simply dropped. Nothing tells you. The task state says RUNNING. The dashboard looks green. Yet some percentage of your data never reached the destination.

!
Watch out

Tolerance “all” without a dead letter queue and without logging is one of the most dangerous settings in Kafka Connect. It turns loud failures into silent data loss. Never use it alone in production.

Log the errors at least

Two settings help you see what is happening:

  • errors.log.enable writes each error into the Connect worker’s log. The default is false.
  • errors.log.include.messages adds details of the failing record to that log, such as its topic, partition, and offset. The default is false, because record contents can be sensitive.

Logging is better than nothing, but it has a weakness: a log line is not the record. You know something failed, but the original bytes are not preserved in a form that you can replay. That is exactly the gap that a dead letter queue fills.

ConfigurationBehavior on a bad recordCan you recover the record?
Tolerance none (default)Task failsYes, it is still in the source topic, but the pipeline is stuck
Tolerance all, no loggingSkipped silentlyOnly by re-reading the source topic, if you even know
Tolerance all, with loggingSkipped, error line loggedOnly by looking up the offset in the source topic
Tolerance all, with DLQSkipped, copy saved in the DLQ topicYes, easily
Everyday analogy

Tolerance “all” alone is like a shop assistant who quietly throws away any item with a torn barcode and never tells the manager. Adding a log is like asking her to write a note. Adding a DLQ is like giving her a box to put the torn items in, so the manager can fix them later.

Retries and Backoff: Trying Again Before Giving Up

Some errors are temporary. Connect can wait and try again before it decides a record is truly bad.

Why retry helps

Not every failure means the record is broken. Sometimes the record is fine and the world around it is having a bad moment. A converter may need to ask a schema registry for a schema, and the registry may be restarting. A destination may be briefly overloaded. If Connect gave up at once, it would send perfectly good records to the DLQ. Retrying gives temporary problems time to pass.

The two retry settings

Setting

errors.retry.timeout

The total time Connect keeps retrying a failed operation. The default is zero, which means no retries. A value of minus one means retry forever.

Setting

errors.retry.delay.max.ms

The longest wait between two attempts. Connect waits a little at first and increases the wait each time, up to this limit. The default is sixty seconds.

This pattern of waiting longer after each failure is called backoff. It avoids hammering a struggling service with a flood of requests.

Everyday analogy

You call a friend and the line is busy. You do not redial every second. You wait a minute, try again, then wait a few minutes, and so on. If the total waiting time passes your limit, you send a message instead. Retry timeout is your limit, and the DLQ is the message.

How retries and the DLQ work together

flowchart TD
    A["Record fails at a station"] --> B{"Retry time left?"}
    B -->|"yes"| C["Wait with backoff then try again"]
    C --> D{"Worked this time?"}
    D -->|"yes"| E["Continue normally"]
    D -->|"no"| B
    B -->|"no"| F{"Tolerance = all?"}
    F -->|"no"| G["Task fails"]
    F -->|"yes"| H{"DLQ configured?"}
    H -->|"yes"| I["Send record to DLQ and continue"]
    H -->|"no"| J["Skip record and continue"]
    
FIG 3 — The decision path for a failed record

Follow the arrows. A failure first asks whether there is retry time left. If so, Connect waits and tries again. When time runs out, the next question is whether errors are tolerated. If not, the task fails. If yes, a configured DLQ receives the record; otherwise the record is skipped.

i
Tip

Retrying makes sense for outside dependencies such as a schema registry or a destination service. It does not help with a truly broken record, because that record will fail the same way each time. If your errors are mostly format problems, keep the retry timeout small.

The Dead Letter Queue Itself

Now we can finally look closely at the tray.

What it is

The dead letter queue is a plain Kafka topic. When a sink connector cannot process a record and errors are tolerated, Connect uses a Kafka producer to write a copy of the original record into that topic. The key and value are saved as the raw bytes that were originally read, so nothing has been changed or lost by the failed conversion.

The settings that control it

SettingWhat it doesDefault
errors.toleranceMust be set to all so the task continues after an errornone
errors.deadletterqueue.topic.nameNames the DLQ topic. Setting this turns the feature onNot set
errors.deadletterqueue.topic.replication.factorHow many copies of the DLQ topic are kept if Connect creates it3
errors.deadletterqueue.context.headers.enableAdds headers that explain why and where the record failedfalse
errors.retry.timeoutTotal time to keep retrying before giving up0
errors.retry.delay.max.msLongest wait between retries60000
errors.log.enableAlso writes each error to the worker logfalse
errors.log.include.messagesIncludes record details in the log linefalse
i
Tip

Always turn on the context headers setting. Without headers, a record in the DLQ is just bytes with no explanation. With headers, each record tells you where it came from and why it failed, which saves hours of detective work.

A minimal mental picture of the configuration

A sink connector with a working dead letter queue needs just three ideas in its configuration: tolerate errors, name a DLQ topic, and add context headers. Optionally, you add logging and a small retry window. That is all. There is no extra service to install, and no separate product to buy.

Real-world picture: one DLQ per connector

Most teams give each sink connector its own DLQ topic, named after the connector, such as dlq-orders-storage-sink. This makes it obvious whose problem a record is, keeps volumes separate, and lets each team set retention and permissions on its own tray.

Context Headers: The Note Attached to Every Bad Record

A failed record with no explanation is a puzzle. Headers turn it into a clue.

What headers are

Every Kafka record can carry small labelled pieces of extra information called headers. They travel beside the key and value. When you turn on the context headers setting, Connect adds a set of headers to each record it writes into the DLQ. The names begin with __connect.errors, so they are easy to recognize.

What the headers tell you

InformationWhy it helps
Original topicTells you which topic the record came from
Original partition and offsetLets you find the exact record in the source topic
Connector name and task IDShows which job and which parallel worker hit the error
StageShows where it failed: a key, value, or header converter, a transformation, or the connector’s write step
Failing class nameShows which converter or transform was running
Exception class and messageGives the short reason, such as “unrecognized token” or “schema not found”
Stack traceGives the detailed technical trail for developers
Everyday analogy

Imagine the “Problems” tray at a post office. A bare envelope in the tray tells you nothing. But if a sticky note says “found at sorting desk 3, at the address-reading step, reason: smudged ink”, you know exactly where to start. The headers are that sticky note.

Using the headers well

  • Group by reason. Count how many DLQ records share the same exception message. Often one cause explains ninety percent of them.
  • Group by origin. Count by original topic and partition to see whether only one producer or one partition is misbehaving.
  • Trace back. Use the original topic, partition, and offset to look at the record in its source, along with the records around it.
  • Watch the stage. A flood of converter errors usually means a format or schema problem. A flood of transformation errors points to a bug or an unexpected null in your own settings.
!
Watch out

Stack traces and exception messages can contain fragments of the record’s contents, such as a customer name inside a parsing error. Treat the DLQ headers as sensitive data and protect the topic with the same care as the original topic.

Architecture and Components

Let us see every part involved when a bad record is caught.

flowchart TD
    subgraph Kafka["Kafka Cluster"]
        SRC["Source topic"]
        DLQ["Dead letter topic"]
        CFG["Connect internal topics: configs, offsets, status"]
    end
    subgraph Worker["Connect Worker Process"]
        CON["Consumer"]
        CNV["Converters"]
        SMT["Transformations"]
        SNK["Sink task logic"]
        EH["Error handling: retry and tolerance"]
        DP["DLQ producer"]
    end
    EXT[("External system")]
    SRC --> CON
    CON --> CNV
    CNV --> SMT
    SMT --> SNK
    SNK --> EXT
    CNV -. "error" .-> EH
    SMT -. "error" .-> EH
    SNK -. "reported error" .-> EH
    EH --> DP
    DP --> DLQ
    
FIG 4 — Components involved in catching and storing a failed record

The diagram has two big boxes. The Kafka cluster holds the source topic, the dead letter topic, and Connect’s own internal topics. The worker process holds a consumer, the converters, the transformations, and the sink task logic. When an error appears at any station, it flows into the error handling layer. That layer decides whether to retry, fail, skip, or hand the record to a DLQ producer, which writes it into the dead letter topic.

Reads

Consumer

A normal Kafka consumer pulls raw records from the source topic.

Decodes

Converters

Turn bytes into structured objects. A frequent source of errors.

Reshapes

Transformations

Optional per-record steps. Another frequent source of errors.

Writes

Sink task logic

The connector code that delivers records to the destination.

Decides

Error handling layer

Applies retry time, tolerance, and logging rules to every failure.

Saves

DLQ producer

A Kafka producer that writes the original record and its headers into the DLQ topic.

Everyday analogy

Think of a quality-control inspector standing beside a production line. Each station can call the inspector when it sees a defect. The inspector decides whether to wait and retry, stop the line, discard the item, or place it in the “needs repair” bin along with a repair note.

Connect’s own internal topics

In distributed mode, Connect stores its own state in Kafka topics: one for connector configuration, one for source offsets, and one for status. These are separate from your DLQ. The DLQ is a data topic that you own, name, and manage, while the internal topics are managed by Connect.

Internal Working: The Journey of a Bad Record

Follow one broken record from arrival to rest in the DLQ.

1

The consumer polls a batch

The task’s consumer reads a batch of raw records. Among them is one whose value is not valid for the converter.

2

The converter fails

The value converter tries to decode the bytes and raises an exception.

3

The error is captured with context

Connect notes the stage, the exception, and the original consumer record with its topic, partition, and offset.

4

Retry rules are applied

If a retry timeout is set, Connect waits and tries again. If it still fails when time runs out, it moves on.

5

Tolerance is checked

If tolerance is none, the task fails here. If it is all, the flow continues.

6

Logging happens if enabled

A line is written to the worker log, with or without record details, depending on your settings.

7

The DLQ producer writes the record

The original key and value bytes are sent to the dead letter topic, with context headers if enabled.

8

The task continues

The failed record is dropped from the batch. The remaining good records go on to the destination, and offsets are committed as normal.

Batches and the write step

Sink tasks usually process records in batches. Converters and transformations run on each record one at a time, so a bad record is removed individually while the rest of the batch is delivered. The connector’s write step then receives only the healthy records.

i
Important insight

The DLQ stores the original bytes as they were read from the source topic. This matters because a failed conversion means Connect never had a clean object. Keeping the raw bytes means you can inspect, repair, and replay exactly what the producer sent.

What happens to ordering?

When a record is diverted, the records after it still flow in their normal order. The bad record simply misses its turn. If you later repair and replay it, it arrives after newer records for the same key. For destinations that care about the newest value per key, replay carefully, and check whether a newer version has already been written.

Everyday analogy

A student’s exam paper is set aside because the name is unreadable. The teacher marks the other papers first. When the name is finally identified, that paper gets marked later, but it still counts. The order of marking changed, not the result, unless the results were being ranked in a way that depends on order.

Errors Inside the Connector: The Errant Record Reporter

Some of the most common problems happen at the last step, when the connector talks to the destination.

The original gap

When the dead letter queue feature first arrived, it covered errors in converters and transformations. But suppose your sink connector writes to a database and the database rejects one row because a text value is too long. That error happens inside the connector’s own code, after converters and transformations have succeeded. The basic error handling could not send such a record to the DLQ, because the connector did not tell Connect which record was at fault.

The fix: reporting bad records from the connector

Later versions of Kafka Connect added a feature for sink connector developers, known as the errant record reporter. It lets a connector say, “I could not process this particular record, and here is the reason.” Connect then applies the usual rules: retry, tolerate, log, and send the record to the DLQ. The connector can keep going with the rest of its batch.

flowchart LR
    B["Batch of 100 records"] --> S["Sink connector code"]
    S -->|"97 accepted"| DB[("Destination")]
    S -->|"3 rejected, reported one by one"| R["Errant record reporter"]
    R --> EH["Connect error handling"]
    EH --> DLQ["Dead letter topic"]
    
FIG 5 — A connector reporting individual rejected records

In the picture, a batch of one hundred records goes to the sink connector. Ninety-seven are accepted by the destination. Three are rejected. The connector reports these three one by one through the reporter, and Connect sends them to the dead letter topic. The other ninety-seven are not held up.

!
Watch out

Not every connector uses the reporter. Support depends on how the connector was written and which version you run. If a connector does not report errors this way, an error inside its write step may still fail the task even with a DLQ configured. Read the connector’s documentation and test with a deliberately bad record.

Connectors with their own error settings

Some connectors also provide their own settings for problems at the destination. For example, a search-engine sink may have a setting for how to treat documents the search engine refuses. These settings work alongside the general Connect error handling. Check which mode each connector supports, and prefer the ones that route bad records into your DLQ over the ones that quietly ignore them.

Where the error happensCaught by generic Connect error handling?Reaches the DLQ?
ConverterYesYes
TransformationYesYes
Connector write, connector uses the reporterYes, via the reporterYes
Connector write, connector does not use the reporterOnly if it throws a retriable errorUsually no; the task may fail

What About Source Connectors?

The DLQ setting exists only for sink connectors, so sources need other plans.

Why there is no source DLQ

A dead letter queue puts the failed Kafka record into another Kafka topic. In a sink, the failed item is already a Kafka record, so saving a copy is natural. In a source, the failed item comes from outside Kafka, such as a database row or a file line. There is no Kafka record yet, and Connect has no general way to store the outside item as one.

For a source connector, the error handling settings still apply to converters and transformations, but the choices are limited to failing the task or skipping the item, with optional logging.

Practical alternatives

Option 1

Pass raw data through

Read the outside data as plain text or bytes, without strict parsing. Do the careful parsing later in a place that can handle failures.

Option 2

Two-stage pipeline

The source connector writes to a “raw” topic. A separate stream job parses records and routes bad ones to its own error topic.

Option 3

Connector-specific handling

Some connectors offer their own way to record or report bad input. Check each connector’s documentation.

Option 4

Fix at the origin

Add validation in the producing system, so bad data never reaches the connector.

flowchart LR
    X[("Outside system")] --> SC["Source connector (light parsing)"]
    SC --> RAW["Raw topic"]
    RAW --> ST["Stream job validates and parses"]
    ST -->|"good"| CLEAN["Clean topic"]
    ST -->|"bad"| ERR["Error topic for this stage"]
    
FIG 6 — A two-stage design that gives a source pipeline its own side tray

The diagram shows a common pattern. The source connector only lifts data from the outside system with minimal parsing, so it rarely fails. A downstream stream processing job does the strict checking. Good records go to a clean topic, and bad ones go to an error topic, which plays the same role as a DLQ.

i
Tip

The habit of moving strict parsing away from the source connector and into a separate stage also helps you react quickly, because you can change the validation rules without touching the connector.

Data Flow and the Life of a DLQ Record

A record in the DLQ has a life story, and a good team knows every chapter.

flowchart LR
    A["Record fails in Connect"] --> B["Stored in DLQ topic"]
    B --> C["Alert fires: DLQ volume above normal"]
    C --> D["Engineer triages using headers"]
    D --> E{"Cause understood?"}
    E -->|"data problem"| F["Repair the record"]
    E -->|"config or code problem"| G["Fix the connector, converter, or schema"]
    E -->|"truly junk"| H["Discard and record the reason"]
    F --> I["Replay to the source topic or destination"]
    G --> I
    I --> J["DLQ record expires by retention"]
    H --> J
    
FIG 7 — From failure to resolution

The path begins when a record fails and lands in the DLQ. An alert tells the team that something arrived. The engineer reads the headers to understand the cause. There are three outcomes. The data may need repair. The pipeline itself may need a fix. Or the record may be truly junk, which is discarded with a note. Repaired or unblocked records are replayed. Eventually, retention removes old DLQ records.

Triage in five questions

1

How many records?

A handful may be isolated bad data. Thousands in minutes suggest a systemic problem.

2

Which stage failed?

Converter, transformation, or connector write. This tells you where to look.

3

What is the exception message?

Group by message. Usually there are only one or two distinct reasons.

4

Who produced them?

Look at the original topic and partition. Often one producer version is responsible.

5

Is it still happening?

If new records keep arriving, fix the cause before replaying, or the DLQ will refill.

Retention: the tray has a time limit

Like any topic, a DLQ deletes old records after its retention time. If your team takes a week to react and the retention is three days, the evidence disappears. Choose a retention long enough to cover holidays, weekends, and slow investigations. Many teams choose a couple of weeks for DLQ topics.

!
Watch out

The source topic also has retention. If the source records expire before you investigate, the DLQ copy may be the only remaining copy. That is one more reason to alert quickly and keep DLQ retention generous.

Reprocessing: Getting Records Back Into the Pipeline

Saving bad records is only half the job. The other half is putting the fixed ones back.

Connect does not replay DLQ records for you. You choose a replay strategy that fits the cause of failure.

Common replay strategies

StrategyHow it worksBest when
Re-send to the original topicA small program reads the DLQ, repairs the record, and produces it back to the source topicThe original connector is fixed and can now read it
Second connector on the DLQA separate sink connector reads the DLQ topic and writes to the destination with lenient settingsYou want the destination to store the raw form for later cleaning
Repair job, then direct writeA stream job or script repairs records and writes them straight to the destinationThe destination is simple and the source topic should not be touched
Manual fixAn engineer corrects a small number of records by handOnly a few records failed
DiscardLeave them to expire, with a recorded reasonThe records are truly junk
flowchart LR
    DLQ["DLQ topic"] --> RJ["Repair job"]
    RJ -->|"fixed record"| SRC["Original source topic"]
    SRC --> CON["Sink connector (now fixed)"]
    CON --> DEST[("Destination")]
    RJ -->|"cannot repair"| PARK["Parking topic for manual review"]
    
FIG 8 — A repair job feeding fixed records back into the pipeline

The repair job reads records from the DLQ, tries to fix each one, and sends good results back to the source topic where the fixed connector picks them up. Records that cannot be repaired go to a parking topic for a person to review. This keeps the DLQ from turning into a permanent pile.

Be careful with duplicates and order

  • Duplicates. If the record partly reached the destination before failing, a replay could create a duplicate. Prefer destinations and settings where writes with the same key replace the earlier version.
  • Order. A replayed record arrives late. If newer data for the same key has been written, the old value could overwrite the newer one. Compare timestamps or version fields before replaying.
  • Loops. If you replay a record before fixing the cause, it will fail again and return to the DLQ. Add a counter or a header to stop endless loops.
Everyday analogy

Returning a corrected parcel to the conveyor belt works only if the belt is now able to handle it. If you put it back before fixing the label reader, it simply lands in the problem tray again. Fix the machine first, then re-send the parcel.

Common Causes of Dead Letters, and How to Spot Them

Most DLQ records come from a short list of causes. Learn the list and triage becomes quick.

CauseWhat you see in the headersUsual fix
Wrong converter for the topicValue converter stage; messages about unexpected bytes or an unknown magic markerMatch the converter to the format the producers really use
Schema not found or mismatchValue converter stage; errors about a missing schema ID or incompatible schemaCheck the schema registry, permissions, and schema compatibility rules
Mixed formats in one topicSome records pass, some fail at the converterSeparate topics by format, or fix the producer
Truncated or corrupted bytesConverter stage; parsing errorsInvestigate the producer or network path; discard if unrecoverable
Null or missing field in a transformTransformation stage; null-pointer style messagesAdjust or guard the transform; handle tombstones and empty values
Destination rejects a valueTask write stage, if the connector reports itWiden the column, clean the data, or filter before writing
Schema registry unavailableConverter stage; connection errors, many at onceRestore the registry; use retries so these are not sent to the DLQ

The special case of tombstones

A tombstone is a record with a key but an empty value. In Kafka, it means “delete this key”, especially in compacted topics. Many transformations and some connectors do not expect an empty value and fail. Records like these are perfectly valid, but they will keep appearing in your DLQ until you handle them, for instance by making the transformation skip empty values or by configuring the connector to treat them as deletes.

Everyday analogy

A blank form marked “cancel this booking” is not a broken form. It is an instruction. A clerk who only knows how to process filled forms will put it in the problem tray, even though it was valid all along.

Bulk failures versus trickles

A trickle (a few records)

  • Usually isolated bad data from one producer
  • The DLQ is doing its job
  • Investigate at normal priority

A flood (thousands quickly)

  • Usually a configuration, schema, or release problem
  • Continuing means filling the DLQ with junk
  • Pause the connector, fix the cause, then resume
i
Tip

Alert on the rate of DLQ arrivals, not only the total. A sudden jump from near zero to hundreds per minute is the sign of a flood that deserves immediate attention.

DLQs Elsewhere: How Connect Compares

The dead letter idea is older than Kafka. Comparing it helps you place Connect’s version correctly.

Classic message queues such as Amazon SQS or RabbitMQ have a dead letter feature built into the broker. After a message fails delivery several times, the broker itself moves it to a special queue. Kafka works differently. Kafka brokers do not track whether a consumer succeeded. They only store records and serve them by offset. So any dead letter behavior in Kafka must be built in the client or framework that reads the records.

SystemWho moves the bad message?Notes
Queue services such as SQSThe broker or service, after a set number of failed deliveriesBuilt in and automatic
Plain Kafka consumerYour own codeYou must write the logic to catch, route, and log
Kafka Connect sinkConnect’s error handling layerBuilt in, configured by settings, sink connectors only
Kafka StreamsException handlers in your applicationHandlers decide to fail or continue; routing to a DLQ topic can be added, and newer versions offer more built-in support, so check your version
Framework libraries such as Spring for Apache KafkaA recoverer component in the frameworkPublishes failed records to a dead letter topic after retries
Everyday analogy

In a traditional post office, the sorting machine has its own built-in problem tray. In Kafka, the warehouse only stores the parcels. The delivery drivers (the clients and frameworks) each bring their own problem tray. Kafka Connect is one type of driver that brings a tray by default when you ask for it.

The “retry topic” cousin

Some teams use a related design with several topics, for example a “retry in one minute” topic, a “retry in ten minutes” topic, and finally the dead letter topic. A failed record hops from one to the next, giving temporary problems time to clear without blocking the main flow. Kafka Connect does not build this ladder for you. It offers in-place retries with backoff and a single DLQ.

Real-world picture: choose the tool that fits

A team loading data into a warehouse uses Connect and its DLQ because it needs no code. Another team processes payments with custom services and builds a retry ladder with several topics, because each step has business meaning. Both use the dead letter idea, at different levels of effort and control.

Choosing Your Error Strategy: A Decision Guide

Walk through these questions when setting up a new sink connector.

1

Is this pipeline brand new or still being tested?

Start with fail-fast so you notice every issue. Move to a DLQ once the data has proven stable.

2

Is the data such that no single record may ever be lost or delayed?

Use fail-fast plus strong alerting, or a DLQ with tight alerting and rapid human response.

3

Can the business live with a few records arriving late?

Use a DLQ with tolerance “all”, context headers, and alerts.

4

Do outside services sometimes fail briefly?

Add a retry timeout with backoff so temporary trouble does not fill the DLQ.

5

Who will watch and handle the DLQ?

Name an owner and a process before turning it on. No owner, no DLQ.

Matching strategy to situation

SituationSuggested setupWhy
New pipeline in developmentTolerance none, logging onSurface every problem early
Analytics feed into storageTolerance all, DLQ with headers, short retry window, alertsFreshness matters and the odd bad record can wait
Payments or ledger sinkTolerance none, or DLQ with instant pagingSkipping a record silently is unacceptable
Data that depends on an external schema registryRetry timeout of a few minutes, then DLQRegistry outages are temporary
Topic with mixed or uncertain formatsTolerance all, DLQ, and a plan to fix the producersKeeps healthy data moving while the mess is cleaned
Load test with junk dataTolerance all, no DLQ, logging offYou do not care about individual records
Decide in advance what a bad record is worth, and set the pipeline to match.

Advantages, Disadvantages, and Trade-offs

A DLQ buys you continuity. It costs you attention.

Advantages

  • One bad record no longer halts the pipeline
  • Original bytes are preserved for study and replay
  • Headers explain why each record failed
  • No custom code needed to start
  • It is an ordinary topic, so all Kafka tools work on it
  • Good records stay fresh and low in lag

Disadvantages

  • Only works for sink connectors
  • Records reach the destination out of order if replayed later
  • Needs someone to watch and act
  • Can hide a systemic problem if alerts are missing
  • Adds a copy of possibly sensitive data in another topic
  • Does not cover every error unless the connector reports it

Trade-offs at a glance

Trade-offFail-fastSkip with log onlyDLQ
Data loss riskNoneHighLow, if watched
Pipeline availabilityLowHighHigh
Recovery effortRestart and fix at onceHard to recover recordsTriage and replay at leisure
Operational loadUrgent pagesHidden until data is missingSteady monitoring
Best fitCritical, low-volume feedsDisposable dataMost production pipelines

The deepest trade-off is latency of attention. Fail-fast makes the problem loud right now. A DLQ makes it quiet now, and important later. If your team is good at watching dashboards and responding within hours, a DLQ is a great fit. If nobody looks at anything for days, a loud failure may protect you better.

Security Considerations

A dead letter queue is a copy of your problem data. Treat it as seriously as the original.

Access

Restrict who can read

The DLQ holds raw records. Give read access only to the small group who triage failures.

Write

Restrict who can write

Only the Connect worker identity should write to the DLQ, so nobody can plant fake records.

Transit

Encrypt connections

Use TLS between workers and brokers, including for the DLQ producer.

Rest

Encrypt storage

Encrypt the disks that hold topic data, since the DLQ can contain personal information.

Personal data in the DLQ

If the source topic contains personal data, so does its DLQ. The DLQ can even hold more risk, because records in it often live longer while people investigate, and because headers can include text from error messages and stack traces. Apply the same retention, deletion, and privacy rules to the DLQ as to the source. If a customer asks for deletion, remember that the DLQ may hold a copy.

!
Watch out

The setting that includes record details in worker logs can leak sensitive data into log files, which are often widely readable. Keep it off unless you have strong log protection, and prefer to look at the DLQ, which you can lock down properly.

Authorization for the Connect worker

The Connect worker identity needs permission to write to the DLQ topic, and to create it if you let Connect create topics. In tightly controlled clusters, create the DLQ topic yourself in advance, with the exact permissions you want, and give the worker only write access. This also avoids surprises in topic settings.

Cleaning up sensitive stack traces

Stack traces help developers but can expose internal class names and library versions. If DLQ topics are shared with a wider audience, such as support staff, consider giving them a cleaned view: a small job that reads the DLQ, removes stack traces, and writes a summary to another topic for wider access.

Real-world picture: a regulated industry

A bank restricts its DLQ topics to an operations group, encrypts them, sets a fixed retention, and audits every read. It treats the DLQ as regulated data, because a failed payment record is still a payment record.

Monitoring, Logging, and Metrics

The DLQ only helps if somebody notices when it fills.

Metrics that Connect gives you

Each task reports error metrics. The names differ slightly between tools, but the ideas are the same.

Metric ideaWhat it tells you
Record errorsHow many records hit an error at any stage
Record failuresHow many failures happened, including repeated ones for the same record
Records skippedHow many records were dropped because errors are tolerated
RetriesHow often Connect had to try again
Errors loggedHow many errors were written to the log
DLQ produce requestsHow many records Connect tried to write to the dead letter topic
DLQ produce failuresHow many times writing to the dead letter topic itself failed
Time of last errorWhen the most recent error happened
flowchart LR
    W["Connect workers metrics"] --> COL["Metrics collector"]
    DT["DLQ topic size and rate"] --> COL
    LG["Worker logs"] --> LP["Log platform"]
    COL --> DB["Dashboards"]
    COL --> AL["Alert rules"]
    LP --> DB
    AL --> ON["On-call team"]
    
FIG 9 — A monitoring pipeline for Connect and its DLQ

Worker metrics and DLQ topic statistics flow into a collector. Worker logs flow to a log platform. Dashboards show trends, and alert rules notify the on-call team when numbers cross a line.

Alerts worth setting

  • Any DLQ arrivals for critical connectors. Even one record may deserve a look.
  • A sudden jump in the arrival rate. This points to a flood, not a trickle.
  • DLQ produce failures above zero. This means Connect could not save a bad record, and that record may be lost.
  • Old records sitting in the DLQ. Alert when the oldest record is approaching the retention limit.
  • Task in the FAILED state. The classic sign of fail-fast or an unhandled error.
  • Growing consumer lag. Slow processing may hide behind a busy retry loop.
i
Tip

Compute a simple ratio: DLQ records divided by records processed. A value of 0.01 percent is a normal trickle for many pipelines. A value of 5 percent is a fire, even if the absolute number looks small.

Reading the DLQ itself

Because the DLQ is an ordinary topic, you can build a small dashboard on it: counts by connector, by stage, by exception message, and by original topic. Many teams build one small consumer that reads the DLQ, extracts these header fields, and writes counts to their metrics system.

Performance and Scalability

A well-behaved pipeline rarely feels the DLQ. A misbehaving one can feel it a lot.

The normal case: almost free

When bad records are rare, the cost of the DLQ is tiny. The DLQ producer sits idle most of the time. A few extra writes per hour do not matter to a cluster that handles thousands of records per second.

The storm case: errors at scale

The picture changes when a large share of records fail. Imagine a wrong converter setting: every record fails. The connector now does extra work for each record: it creates an error context, retries (if configured), logs (if enabled), and produces the record to the DLQ. That means the pipeline can slow down and the DLQ topic can grow as fast as the source topic.

Tiny
Cost when failures are rare
Doubled
Storage in a total failure storm
Minutes
To notice with rate alerts

Keeping storms small

  • Alert on the rate and pause the connector quickly during a flood.
  • Turn off record details in logs so a storm does not also flood log storage.
  • Size the DLQ generously, with enough partitions and disk to absorb an unexpected burst.
  • Avoid long retry windows for permanent errors. A retry that cannot succeed only slows the pipeline. Keep retries for external services.
  • Test with bad data before launch to learn how the pipeline behaves.
Everyday analogy

A small problem tray is never noticed at a post office. But if a whole van of misaddressed mail arrives, the tray overflows, the staff spend all day on paperwork, and normal mail slows down. The answer is to spot the van early and stop unloading it.

Scaling the DLQ with the pipeline

If a connector runs many tasks, each task writes to the same DLQ topic. Choose a partition count that supports the combined worst-case rate. Because DLQ records are usually read by a low-volume triage process, you may not need many partitions for consumers, but keep enough to spread the write load.

Reliability and High Availability

The tray must not have holes. If it fails, bad records vanish.

The DLQ is a Kafka topic, so use Kafka’s protections

Copies

Replication factor

Keep several copies of every DLQ record so a broker failure does not lose them. The Connect default of three is a sensible choice.

Safety

Minimum in-sync copies

Require more than one copy to acknowledge each write, so a record is not accepted onto a single fragile broker.

Time

Retention

Keep records long enough for people to act, including weekends and holidays.

Space

Disk headroom

Plan disk for a burst, so a flood does not fill the cluster.

What if writing to the DLQ fails?

Writing to the DLQ is a normal Kafka produce call, and it can fail, for example when the topic does not exist and cannot be created, when permissions are missing, or when the brokers are unreachable. In such cases Connect logs the problem and counts it in the “DLQ produce failures” metric. The record that could not be saved may be lost, since it has already been skipped. This is why the produce failure metric deserves an alert of its own, and why the topic and permissions should be prepared in advance.

!
Watch out

A DLQ configured with a topic name that the worker cannot write to gives a false sense of safety. Test the whole path once, on purpose, with a deliberately bad record, and confirm it appears in the topic with headers.

Worker and task failures

Connect itself is built for reliability. If a worker dies, its tasks move to other workers, and they resume from the last committed offsets. Records that were already sent to the DLQ stay there safely in Kafka. Records that were in flight may be processed again after the move, so a bad record might be written to the DLQ more than once. Design your triage and replay steps to tolerate duplicates.

flowchart LR
    W1["Worker A runs task 1"] --> DLQ["DLQ topic replicated on three brokers"]
    W2["Worker B runs task 2"] --> DLQ
    W1 -. "worker fails" .-> X["Task 1 moves"]
    X --> W3["Worker C resumes task 1 from committed offset"]
    W3 --> DLQ
    
FIG 10 — Tasks can move between workers while the DLQ stays safe

The picture shows two workers writing to a replicated DLQ topic. When worker A fails, its task moves to worker C, which resumes from the last committed offset. The DLQ keeps every record already written, and the resumed task may re-send a small number of records, which is why duplicates are possible.

Deployment and Cloud

A few simple habits make DLQs predictable across environments.

Treat error settings as code

Keep connector configurations in source control, not only in a running system. The error handling settings belong right beside the rest of the connector’s settings, with comments that explain the choices. Reviews then catch dangerous combinations, such as tolerance “all” without a DLQ.

Naming conventions

ChoiceExampleBenefit
One DLQ per connectordlq-orders-storage-sinkClear ownership and separate volumes
One DLQ per teamdlq-payments-teamFewer topics but mixed sources; rely on headers to separate
Prefix with environmentprod-dlq-orders-sinkAvoids mixing test and production records

Creating the topic ahead of time

Connect can create a missing DLQ topic if permissions allow, but production teams normally create it in advance. That way they choose the partition count, retention, replication, minimum in-sync copies, and permissions, instead of accepting defaults.

Managed and cloud services

Cloud teams have several ways to run Connect. Amazon MSK Connect and managed Connect offerings from Kafka vendors run standard Kafka Connect underneath, so the same error settings apply. Some managed connectors expose dead letter queue options in their own screens or configuration forms. Details differ between providers and connectors, so check the current documentation for the service you use.

1

Create the DLQ topic

With chosen partitions, retention, replication, and permissions.

2

Deploy the connector with error settings

Tolerance, DLQ name, headers, retries, and logging, all reviewed.

3

Send a deliberately bad record

Confirm it lands in the DLQ with headers and that the pipeline keeps flowing.

4

Verify alerts and dashboards

Confirm the alert fires and the on-call person knows what to do.

5

Document the runbook

Write down the triage and replay steps so anyone can follow them.

Everyday analogy

A fire drill checks that alarms ring, exits open, and everyone knows where to go, before a real fire. Sending a test bad record is your fire drill for the dead letter queue.

Design Patterns and Anti-Patterns

The same good ideas and the same bad ideas show up in every team.

Patterns that work

Pattern

Tolerate, save, alert

Keep the pipeline moving, save every failure in a DLQ, and alert people on arrivals.

Pattern

Retry only for the outside world

Use short retry windows with backoff for registries and destinations, not for permanently broken records.

Pattern

DLQ per connector

Clear ownership, separate volumes, easy cleanup.

Pattern

Repair and replay job

A small program that fixes known problems automatically and returns records to the pipeline.

Pattern

Parking topic

Records that cannot be repaired move from the DLQ to a parking topic with a reason attached, so the DLQ stays small.

Pattern

Validate at the door

Check schema and rules in producers, so fewer bad records enter Kafka in the first place.

Anti-patterns to avoid

ANTI-PATTERN 01Avoid
What people do

They set tolerance to all and configure no DLQ and no logging.

Why it hurts

Bad records vanish silently. The task looks healthy while data goes missing.

Better approach

Always pair tolerance “all” with a DLQ, headers, and alerts.

ANTI-PATTERN 02Avoid
What people do

They create a DLQ and never look at it.

Why it hurts

Records pile up until retention deletes them. The pipeline appears fine while data is permanently lost.

Better approach

Assign an owner, set alerts, write a runbook, and review the DLQ regularly.

ANTI-PATTERN 03Avoid
What people do

They leave context headers off to save a little space.

Why it hurts

Each DLQ record becomes a mystery. Investigators must guess the reason and hunt for the original.

Better approach

Turn headers on. The space cost is small compared with the time saved.

ANTI-PATTERN 04Avoid
What people do

They replay the whole DLQ into the source topic before fixing the cause.

Why it hurts

The same records fail again, refill the DLQ, and can create an endless loop.

Better approach

Fix the cause first, replay a small test batch, and only then replay the rest.

ANTI-PATTERN 05Avoid
What people do

They put the DLQ topic on default settings with short retention and a single copy.

Why it hurts

The safety net is weaker than the pipeline it protects. A broker failure or a weekend can erase the evidence.

Better approach

Replicate, set a sensible minimum in-sync count, and choose retention that covers slow investigations.

ANTI-PATTERN 06Avoid
What people do

They use the DLQ as a permanent data store or as a place to send normal business rejections.

Why it hurts

It grows without limit and mixes technical failures with business events, making both harder to handle.

Better approach

Use the DLQ for technical failures only. Route expected business rejections to their own topic with clear meaning.

Best Practices and Common Mistakes

Small habits that prevent most dead letter surprises.

Habits that pay off

1

Decide the policy per connector

Write down what a bad record is worth and choose fail-fast or DLQ on purpose.

2

Always enable context headers

They are the difference between a clue and a mystery.

3

Create the DLQ topic yourself

Choose partitions, retention, replication, and permissions before launch.

4

Alert on arrivals and on produce failures

Both the tray filling and the tray breaking need a notification.

5

Rehearse with a deliberately bad record

Prove the full path works before real data depends on it.

6

Write a runbook

Triage, repair, replay, and discard steps, with names of the people responsible.

7

Fix root causes, not only symptoms

A DLQ that fills every week is a signal that a producer or a schema needs attention.

Common mistakes

Mistake

Forgetting tolerance

Setting a DLQ name without setting tolerance to all. The task still fails on the first error.

Mistake

Expecting source DLQ

Trying the setting on a source connector, where it does not apply.

Mistake

Assuming every error is caught

Errors inside a connector’s own write step may not reach the DLQ.

Mistake

Retrying broken records for hours

Long retries on permanent errors just delay the pipeline.

Mistake

Wrong converter blamed on data

Filling the DLQ with perfectly good records because the converter does not match the topic format.

Mistake

Ignoring tombstones

Valid delete markers landing in the DLQ because a transform cannot handle empty values.

i
Tip

The first time you see a flood of DLQ records after a release, check the boring things first: the converter settings, the schema registry address, and any recent producer change. Most floods come from a configuration mismatch, not from truly corrupt data.

A Guided Walkthrough: Loading Orders Safely

Let us design the error handling for one real pipeline from start to finish.

Brightcart, an online store, publishes an orders topic. A sink connector copies orders into a cloud storage bucket every few minutes, where analysts query them. The connector uses an Avro converter with a schema registry, and one transformation that adds a “received time” field.

flowchart LR
    APP["Mobile and web apps"] --> ORD["orders topic"]
    ORD --> SNK["Storage sink connector: tolerance all, retry 5 minutes"]
    SNK -->|"good records"| S3[("Cloud storage bucket")]
    SNK -->|"bad records with headers"| DLQ["dlq-orders-storage-sink"]
    DLQ --> AL["Alert on arrivals"]
    DLQ --> RJ["Repair job"]
    RJ -->|"fixed"| ORD
    RJ -->|"unfixable"| PK["parking-orders topic"]
    
FIG 11 — Brightcart’s orders pipeline with a complete error path
1

Decide the policy

Analytics values freshness, and a few late records are fine. So the team chooses tolerance “all” with a DLQ.

2

Prepare the topic

They create dlq-orders-storage-sink with three copies, a minimum of two in-sync copies, and two weeks of retention. Only the Connect identity can write to it.

3

Set retries

The schema registry sometimes restarts during upgrades, so they allow five minutes of retries with backoff before a record is treated as bad.

4

Turn on headers and logging

Headers are enabled. Error logging is enabled, but record details are left out of the logs.

5

Rehearse

They publish one deliberately malformed order to a test topic and confirm it appears in the DLQ with all headers while good orders keep landing in storage.

6

Add alerts

Any DLQ arrival sends a message to the data team. A rate above ten per minute pages the on-call engineer. Any DLQ produce failure pages immediately.

A week in production

On Monday, a new mobile release accidentally sends order values as text instead of numbers for a small share of users. Fifty orders reach the DLQ. The alert fires, and an engineer groups the DLQ records by exception message. One message dominates: a type mismatch. The original topic and partition point to a single app version.

On Tuesday, the mobile team ships a fix. The data team writes a repair job that converts the text values to numbers and sends the fixed orders back to the orders topic. They replay ten records first, check the results in storage, and then replay the rest. Analysts see complete data, and nobody had to stop the pipeline.

On Thursday, the registry is restarted for an upgrade. For four minutes, the converter cannot fetch schemas. Because retries are set for five minutes, nothing reaches the DLQ. Records are simply delayed, then processed.

EventWhat the design did
Malformed orders from a new app versionDiverted to the DLQ; the pipeline kept running; headers pointed to the cause
Schema registry restartRetries absorbed the outage; no false DLQ records
Wrong converter setting after a changeRate alert fired within minutes; the connector was paused and fixed
DLQ topic permission removed by mistakeDLQ produce failure alert fired; permission restored before more records were lost
Truly corrupted recordMoved to the parking topic with a reason; expired later

Myths and Misconceptions

Wrong ideas about dead letter queues travel from team to team. Here are the corrections.

Myth

“Kafka has a built-in dead letter queue.”

Kafka brokers do not. Frameworks and tools such as Connect add the behavior on the client side.

Myth

“Naming a DLQ topic is enough.”

You must also set tolerance to all, otherwise the task still fails on the first error.

Myth

“The DLQ catches every possible error.”

It covers converters and transformations, and connector errors only when the connector reports them.

Myth

“A DLQ means we never lose data.”

Not if nobody watches it, if retention is too short, or if writing to the DLQ itself fails.

Myth

“Source connectors can use the same setting.”

The DLQ setting applies to sink connectors only.

Myth

“Replaying is automatic.”

Connect never replays for you. You design the repair and replay step.

i
Good to know

Think of a DLQ as a smoke detector, not a fire extinguisher. It tells you something is wrong and keeps the evidence. People still have to come and deal with the fire.

Real-World and Industry Patterns

These situations appear again and again wherever teams move data with Kafka Connect.

Loading a data lake

Companies copy event topics into cloud storage for analysis. Producers come from many teams, so occasional malformed events are certain. Teams run sink connectors with a DLQ so the lake stays fresh, and they review the DLQ daily to find which producer needs help.

Feeding a search engine

Sink connectors that write documents into a search engine meet rejected documents, such as fields with the wrong type. A DLQ keeps the search index updating and preserves the rejected documents for repair.

Writing to relational databases

Sink connectors that insert into database tables run into length limits and constraint violations. Teams use connectors that report individual rejected rows, so only the offending rows land in the DLQ, and the rest of the batch is written.

Regulated industries

Financial and healthcare teams often prefer fail-fast, or a DLQ with paging on every arrival, because a skipped record can be a compliance problem. They also encrypt and audit the DLQ like any sensitive topic.

Migrations and schema changes

During schema changes, some records use the old shape and some the new. A DLQ acts as a safety valve while producers and consumers catch up, and the arrival rate becomes a useful measure of migration progress.

A quiet DLQ means a healthy pipeline only if someone is checking that it works.

Operating the Pipeline: Pause, Restart, and Inspect

Knowing the everyday controls turns a stressful incident into a routine job.

Kafka Connect in distributed mode comes with a small web interface, often called the REST interface, for managing connectors. You use it to look at what is happening and to change it. During a dead letter incident, you will use it in four situations.

Look

Check status

See whether a connector and each of its tasks are running, paused, or failed. A failed task shows the error trace that stopped it.

Stop the flow

Pause

Pause a connector during a flood, so it stops consuming and stops filling the DLQ, while you investigate. Records wait safely in the source topic.

Start again

Resume

Resume after the cause is fixed. The connector continues from its saved position.

Recover

Restart

Restart a failed task, or a whole connector. Newer versions let you restart only the failed parts, which is gentler on healthy tasks.

Everyday analogy

The REST interface is the control panel at the side of the conveyor belt. It has a status display, a pause switch, a resume switch, and a reset button for jammed stations. A calm operator uses them in order: look, pause, fix, resume.

Changing settings safely

When you change a connector’s configuration, such as adding a DLQ or fixing a converter, Connect stores the new configuration and restarts the connector’s tasks so that they pick it up. During that moment, tasks stop and restart, which is a small pause in processing. Plan changes for a quiet time when you can, and check the status afterwards to confirm that every task returned to the running state.

Where the connector’s position is stored

A sink connector’s position in its source topic is stored in the normal Kafka consumer offsets for a group that Connect creates for that connector. This has a useful consequence. When you fix a problem and resume, the connector continues where it left off, and no records are skipped or repeated, apart from the small number that may be repeated after an unclean stop. Newer versions of Kafka Connect also allow a stopped connector’s offsets to be viewed and changed through the same interface, which can help in rare cases where you need to move the connector’s position deliberately. Use this carefully, because moving the position forward skips records and moving it backward repeats them.

1

Look at the status

Is the connector running, and are all tasks running? Note the times and any error trace.

2

Pause if the DLQ is flooding

Stopping the flow protects the DLQ from filling with junk and gives you time.

3

Find the cause

Read the DLQ headers, the worker log, and any recent changes to producers or settings.

4

Apply the fix

Correct the configuration, the schema, or the producer. Test with a small sample if you can.

5

Resume or restart

Bring the connector back and watch the DLQ arrival rate fall to normal.

6

Replay the saved records

Repair and replay the DLQ records that were caught during the incident.

i
Tip

Write this six-step routine into your team’s runbook, with the exact commands or screens your team uses. When an alert fires at night, a short checklist beats a clever memory every time.

Reading the worker log

The worker log shows what the connector was doing when an error happened. With error logging enabled, each failure produces a line naming the stage, the exception, and the location of the record. Combined with the DLQ headers, it lets you rebuild the story: which record, which step, which reason, and at what time. Keep logs long enough to cover your investigation window, and remember that log lines with record details can hold sensitive data.

Frequently Asked Questions

Q1What is a dead letter queue in Kafka Connect?
It is a Kafka topic where a sink connector’s error handling layer stores copies of records that it could not process, so the connector can continue with the remaining records.
Q2Which connectors support it?
Sink connectors only. Source connectors have no built-in dead letter queue.
Q3How do I turn it on?
Set errors.tolerance to all, and give a topic name in errors.deadletterqueue.topic.name. It is a good idea to also enable context headers.
Q4Why does my task still fail even though I named a DLQ topic?
Most often because tolerance is still none. It can also happen when the error is inside the connector’s write step and the connector does not report errors to Connect.
Q5What exactly is stored in the DLQ?
The original key and value bytes of the failed record, plus optional headers that describe where and why it failed.
Q6Should I always use tolerance “all”?
No. For critical feeds where any skipped record is unacceptable, fail-fast with strong alerting may be better. Choose based on what a bad record is worth to your business.
Q7What is the difference between retries and the DLQ?
Retries try the same operation again to overcome temporary problems. The DLQ stores a record that still failed after retries, or that failed in a way retries cannot fix.
Q8Does Connect create the DLQ topic for me?
It can, if it has permission, using the replication factor you set. Production teams usually create it themselves in advance to choose partitions, retention, and permissions.
Q9How long do records stay in the DLQ?
As long as the topic’s retention allows. Set it long enough for people to investigate, and remember that the DLQ is an ordinary topic that will delete old records.
Q10Can I replay DLQ records automatically?
Connect does not do it for you. You build a repair and replay process, such as a small job that fixes records and produces them back to the source topic.
Q11What happens if writing to the DLQ fails?
Connect logs the problem and counts it in a metric. The bad record may be lost, so alert on that metric and prepare the topic and permissions in advance.
Q12Do valid delete markers (tombstones) go to the DLQ?
They can, if a transformation or connector cannot handle empty values. Adjust the transformation to skip them or handle them as deletes.
Q13Should each connector have its own DLQ?
Usually yes. It gives clear ownership and separate volumes. A shared DLQ is possible, but you then rely on headers to tell records apart.
Q14Is the DLQ safe to share with wider teams?
Treat it as sensitive. It holds raw records and headers that can include fragments of data. Restrict access, encrypt it, and consider giving wider teams a cleaned summary instead.
Q15How do I test that my setup works?
Send one deliberately malformed record through a test version of the pipeline. Confirm that it appears in the DLQ with headers, that good records keep flowing, and that the alert fires.
Q16Will a DLQ slow my pipeline down?
Not when failures are rare. In a large failure storm, the extra work per record can slow the pipeline, so alert on the arrival rate and pause the connector quickly.
Q17Can the same record end up in the DLQ twice?
Yes, after a worker failure or task move, a small number of records may be processed again. Make triage and replay steps safe to repeat.
Q18What should I do first when the DLQ suddenly fills up?
Check the arrival rate. If it is a flood, pause the connector, read the headers to find the stage and the exception message, look for recent changes in converters, schemas, or producers, fix the cause, and only then resume and replay.
Q19Is a DLQ the same as the “error topic” I built in my own stream job?
They play the same role, a side tray for records that could not be processed, but they are created differently. Connect’s DLQ is built in and configured by settings. A stream job’s error topic is something your own code writes, so you decide the format, headers, and rules.
Q20How many records in the DLQ is too many?
There is no universal number. Compare it with the volume the connector processes. A tiny fraction of a percent may be a normal trickle for your data, while several percent means a systemic cause that needs attention. Track your own normal level and alert on changes from it.
Q21Can I stop records from reaching the DLQ during a planned schema migration?
Plan the migration so that old and new shapes are both readable by the connector’s converter, for example with compatible schema changes. If that is not possible, expect a burst of DLQ records during the switch, keep retention generous, and prepare a repair job in advance.

Summary and Key Takeaways

A dead letter queue in Kafka Connect is a practical answer to a simple problem: one bad record should not stop a whole pipeline. By setting aside records that a sink connector cannot process, Connect keeps healthy data flowing and keeps the failed records safe for study and repair. The idea is easy, but it works well only when the surrounding habits are in place: tolerance, headers, retries, alerts, owners, and a plan for replay.

Key Takeaways

  • What it is: an ordinary Kafka topic that receives copies of records a sink connector could not process.
  • How to enable it: set tolerance to all, name a DLQ topic, and turn on context headers so every record explains itself.
  • Sink only: source connectors have no built-in DLQ, so use a two-stage design or connector-specific options.
  • Know what is covered: converters and transformations are covered automatically, and connector write errors only when the connector reports them.
  • Retry for the outside world: use short retries with backoff for registries and destinations, not for permanently broken records.
  • Never skip silently: tolerance “all” without a DLQ or alerts is silent data loss.
  • Own the tray: create the topic yourself, replicate it, secure it, set retention that covers slow investigations, alert on arrivals and on produce failures, and write a runbook for triage, repair, and replay.