You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@camel.apache.org by "Claus Ibsen (Jira)" <ji...@apache.org> on 2026/07/20 18:12:00 UTC

[jira] [Resolved] (CAMEL-24208) pollOnError=ERROR_HANDLER skips valid messages on other partitions due to SeekUtil bug

     [ https://issues.apache.org/jira/browse/CAMEL-24208?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Claus Ibsen resolved CAMEL-24208.
---------------------------------
    Fix Version/s: 4.22.0
       Resolution: Duplicate

Is fixed by CAMEL-23994

> pollOnError=ERROR_HANDLER skips valid messages on other partitions due to SeekUtil bug
> --------------------------------------------------------------------------------------
>
>                 Key: CAMEL-24208
>                 URL: https://issues.apache.org/jira/browse/CAMEL-24208
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-kafka
>    Affects Versions: 4.21.0
>            Reporter: Joseph Tam
>            Priority: Minor
>             Fix For: 4.22.0
>
>
> *Bug Summary:*
> When `pollOnError` is set to `ERROR_HANDLER` (the default) and a consumer is assigned multiple partitions, a `RecordDeserializationException` on one partition causes `SeekUtil` to improperly increment the offset of _every_ partition assigned to that consumer, silently skipping valid messages.
> *Technical Details:*
> If a `RecordDeserializationException` is thrown inside the `poll()` loop, `KafkaFetchRecords` catches it globally as an `Exception` and hardcodes the `partitionLastOffset` to `-1L`:
> {code:java}
> // KafkaFetchRecords.java
> catch (Exception e)
> { long partitionLastOffset = -1L; this.pollExceptionStrategy.handle(partitionLastOffset, e); }
>  
> {code}
> It then delegates to `BridgeErrorStrategy.handle(long partitionLastOffset, Exception exception)`. Because `partitionLastOffset` is `-1L`, `BridgeErrorStrategy` passes `-1L` down to `SeekUtil.seekToNextOffset`.
> When `SeekUtil` receives `-1L`, it loses track of which partition actually failed. To recover, it iterates over `consumer.assignment()` (every single partition assigned to the consumer) and arbitrarily advances their fetch positions by 1:
> {code:java}
> // SeekUtil.java
> if (tps != null) {
> for (TopicPartition tp : tps)
> { long next = consumer.position(tp) + 1L; // ... consumer.seek(tp, next); }
> } 
> {code}
> *Impact:*
> If a consumer thread owns multiple partitions (e.g., 4 consumers processing a topic with 16 partitions), a bad message on `Partition-0` will cause the consumer to seek past the bad message on `Partition-0`, but it will _also_ advance the offset of `Partition-1`, `Partition-2`, and `Partition-3` by 1.
> If there were valid messages staged at the current offset of those other partitions, they are permanently skipped and lost without being processed.
> *Proposed Fix:*
> `BridgeErrorStrategy` (or `SeekUtil`) should intercept `RecordDeserializationException` (or similar exceptions that expose the partition) to extract the exact `TopicPartition` and offset provided by the Kafka client, and strictly seek _only_ that specific partition forward.
> For example, modifying `BridgeErrorStrategy`:
> {code:java}
> if (partitionLastOffset == -1L && exception instanceof RecordDeserializationException) {
> RecordDeserializationException rde = (RecordDeserializationException) exception;
> TopicPartition tp = rde.topicPartition();
> long offset = rde.offset();
> if (tp != null && offset >= 0)
> {
> this.consumer.seek(tp, offset + 1L); return; // skip calling SeekUtil.seekToNextOffset }
> }
> {code}
>  



--
This message was sent by Atlassian Jira
(v8.20.10#820010)