ConnectorGetConfig icon
Request icon
ConnectorGetStatus icon
TopicList icon
OutputValues icon
Log icon
If icon
SlackIncomingWebhook icon
Pause icon
Fail icon
TopicCreate icon
TopicDescribe icon
ConnectorUpdateConfig icon
LoopUntil icon
Schedule icon

Prevent Kafka Connect sinks from dropping malformed records

Find Kafka Connect sinks that skip bad records without a DLQ, provision a retained topic and verify the new config behind approval.

Categories
Data

Safety

Replacing a connector config restarts its tasks. DLQ records contain the original bytes and error context, which can expose sensitive data. Restrict both DLQ access and Kestra execution access: the saved config can contain credentials.

Guard What it stops
One exact connector, sink type, errors.tolerance=all and empty DLQ Changing sources, fail-fast sinks or an existing error-routing policy
RUNNING connector with nonempty RUNNING tasks Restarting an intentional pause or acting on an already failed pipeline
Two mode inputs and explicit Pause approval An audit creating a topic or restarting tasks
Full config and status rechecks after approval and before replacement Overwriting a plan that changed while approval or provisioning waited
Reserved topic name, shape and retention checks Adopting a topic that would immediately expire or compact rejected records
Exact config readback, topic existence and task health Reporting an accepted but ineffective config update as a repair

How it works

  1. read_config (io.kestra.plugin.kafka.ConnectorGetConfig), read_plugins (io.kestra.plugin.core.http.Request), read_status (ConnectorGetStatus) and list_topics (TopicList) audit the selected connector. assess requires a canonical sink class, tolerance all, an empty or whitespace-only DLQ name and running tasks.
  2. unprotected_sink posts the gap to Slack. live_mode requires auto_remediate=true and dry_run=false; approve waits for approved=true. Declining ends without a write.
  3. fresh_config and fresh_status reject a stale approval. provision_dlq (TopicCreate) creates kestra-dlq-<connector_name> idempotently. inspect_dlq (TopicDescribe) checks one partition, the requested replication factor, delete cleanup, seven-day retention and no byte retention cap. Existing topics are never altered.
  4. before_update and before_update_status recheck eligibility. install_dlq (ConnectorUpdateConfig) preserves the current config and adds the topic name, context headers and replication setting. await_tasks polls for up to 12 iterations at five-second intervals. verify_config, verify_topics and verify_result require the exact expected config and a visible topic. Any error alerts Slack.

What you get

A visible warning for a silent-loss configuration, a dedicated retained DLQ and a verified config change. A failed partial pass leaves its topic available for a reconciled retry.

Who it's for

Platform teams responsible for Kafka Connect sinks where malformed input is tolerated so healthy traffic can continue.

Why orchestrate this with Kestra

The incident needs an audit, a human decision, broker provisioning and a connector restart across two APIs. Kestra keeps that approval and its snapshot with the execution, queues overlapping runs and alerts when the readback fails.

Tested end to end

Kestra 2.0.5, Kafka plugin 1.10.5, Slack plugin 2.1.0 and Confluent Kafka/Connect 7.9.0. FileStream sinks used JsonConverter without schemas. Slack was the existing local HTTP sink.

Before repair, kafka-consumer-groups --describe showed committed offset 5, end offset 5 and lag 0. The sink contained only the two valid records from five inputs; three malformed records had no DLQ. The connector and task stayed RUNNING. After repair, a new valid record reached the sink and a new malformed record appeared once in the DLQ. kafka-console-consumer read its original bytes with topic and offset headers; kafka-get-offsets reported DLQ end offset 1. Previously skipped records were not recovered.

Case Execution Result
Existing DLQ Qgc04DHgHKcJCLJZ0Dk9v SUCCESS; existing topic and header policy preserved
Dry run 3QgqARsgaNKE1CkmWQB1Xr SUCCESS; auto_remediate true, dry_run true, no writes
Declined approval 2X51aYrxoS54uhqGwt5IyV SUCCESS; config unchanged
Remediation 6WiyDTd7V6PaNbgjjI6DcH SUCCESS; one valid row delivered and one malformed record retained
Repeat 57aAXsFKTk4eHEWU3HS34K SUCCESS; no topic or config writes
Refused config PUT 713tfKpHbTUP6VYJ6sEtrW FAILED as intended; created topic retained
Partial recovery 4PcFCEGS382zoLkncWKJPQ SUCCESS; existing topic reused and delivery proved
Config edit during approval 60nROK53WKdFxJRhwdCzmr FAILED as intended; operator edit preserved, no writes
Tolerant source 376y2sbYCwmaoll7gUu9Ra SUCCESS; no writes
Fail-fast sink 6dM4xp8qUanBmOHnJrjtV3 SUCCESS; no writes
Intentional pause 2E7IWfJmHVhRdw52YbugRY SUCCESS; no writes
Automation disabled 6CHKGSAUeC7fXk6qzeg07v SUCCESS; dry_run false, auto_remediate false, no writes
Whitespace DLQ name 3DWXtf4LJzTXm2nCGzRF5e SUCCESS; treated as missing
Wrong-retention lookalike 2CHP666vpyin8blq66664v FAILED as intended; topic and connector untouched
Three replicas on one broker 3UrHGTCStiZrByjrhizZZN FAILED as intended; no connector write
HTTP 200 without DLQ keys 2xn6oajzprPvz8XQaJJ8fR FAILED as intended; config readback caught the ineffective update

A second fresh broker/worker pair reproduced the baseline and repeated healthy 4EiF4kgXZkUbFdpiUiql63, remediation 5MmjGFiigKtlZevfo7PXgO, race_or_partial qGOzz1iLo9rQEp5Ruwkzc on the same runtime bytes. The no-effect test forwarded a genuine config PUT with the DLQ keys stripped: Connect returned HTTP 200, then the real config readback failed verification. All readbacks came from the real worker.

Prerequisites

Kafka and a Kafka Connect worker on that same cluster, the Kafka plugin and a Slack incoming webhook. Reserve the kestra-dlq- prefix. Connector names must form valid Kafka topic names after the prefix is added. connector.class must use the canonical class name listed by /connector-plugins.

Kestra needs Connect config/status reads, plugin inventory reads and config update permission on the named connector. Its Kafka principal needs Describe, DescribeConfigs and Create for the reserved DLQ names. The worker's principal needs Write and Describe on the DLQ. Configure authentication/TLS properties on all relevant Kafka tasks and credentials on both Connect tasks and the HTTP inventory request for a secured deployment.

Secrets

  • KAFKA_CONNECT_URL: worker REST base URL.
  • KAFKA_BOOTSTRAP_SERVERS: brokers used by that worker.
  • SLACK_WEBHOOK_URL: incoming webhook for findings and failures.

Inputs

Input Default Purpose
connect_url Secret Connect REST URL without a trailing slash
bootstrap_servers Secret Worker cluster's brokers
connector_name Required One exact connector
dlq_replication_factor 3 Approved replica policy; 1 only for a single-broker lab
auto_remediate false Permit a change to reach approval
dry_run true Keep every write disabled
slack_webhook_url Secret Webhook; permits a local sink for testing

Quick start

  1. Add the secrets and reserve the DLQ topic prefix with suitable ACLs. Confirm the broker list belongs to the worker.
  2. Set connector_name and run with the defaults. Check the sink, tolerance, missing-DLQ and running flags in report.
  3. Set a replication factor that fits the cluster. Run with auto_remediate=true and dry_run=false, then approve the task restart with approved=true.
  4. Send a controlled malformed record and read it from the DLQ with its context headers. Confirm that a valid record still reaches the destination before closing the incident.
  5. To schedule audits, replace the trigger's example connector name. The daily Schedule ships disabled and forces both safe-mode inputs.

How to extend

Change retention only by updating both provision_dlq.configs and check_topic_shape. Existing topics with another policy are refused rather than modified. Add destination-specific canaries before treating this as proof for a different sink implementation.

Limits: one connector per execution. Existing nonempty DLQ names are preserved even when their topics are absent; Connect normally attempts to create a configured missing DLQ itself. This flow prevents future converter-error loss and does not replay skipped records. Concurrency is one for this deployed flow only. Kafka Connect has no conditional config PUT or stable incarnation ID, so coordinate external config writers and connector deletion throughout live runs; the last-read-to-write interval cannot be made atomic here. The flow verifies config, topic policy and reported task health. Canary delivery was proved independently for FileStream/JsonConverter. Authenticated clusters, replica failures, other sinks and real Slack delivery were not exercised.

Links

This blueprint was created by zkasuran.

See How

New to Kestra?

Use blueprints to kickstart your first workflows.