{"id":"40dd912c7349f952","repo":"apache/kafka","slug":"received-exception-when-fetching-the-next-record-f","errorCode":null,"errorMessage":"Received exception when fetching the next record from {}. If needed, please seek past the record to continue consumption.","messagePattern":"Received exception when fetching the next record from (.+?)\\. If needed, please seek past the record to continue consumption\\.","errorType":"exception","errorClass":"KafkaException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/consumer/internals/CompletedFetch.java","lineNumber":262,"sourceCode":"    }\n\n    /**\n     * The {@link RecordBatch batch} of {@link Record records} is converted to a {@link List list} of\n     * {@link ConsumerRecord consumer records} and returned. {@link BufferSupplier Decompression} and\n     * {@link Deserializer deserialization} of the {@link Record record's} key and value are performed in\n     * this step.\n     *\n     * @param fetchConfig {@link FetchConfig Configuration} to use\n     * @param deserializers {@link Deserializer}s to use to convert the raw bytes to the expected key and value types\n     * @param maxRecords The number of records to return; the number returned may be {@code 0 <= maxRecords}\n     * @return {@link ConsumerRecord Consumer records}\n     */\n    <K, V> List<ConsumerRecord<K, V>> fetchRecords(FetchConfig fetchConfig,\n                                                   Deserializers<K, V> deserializers,\n                                                   int maxRecords) {\n        // Error when fetching the next record before deserialization.\n        if (corruptLastRecord)\n            throw new KafkaException(\"Received exception when fetching the next record from \" + partition\n                    + \". If needed, please seek past the record to \"\n                    + \"continue consumption.\", cachedRecordException);\n\n        if (isConsumed)\n            return Collections.emptyList();\n\n        List<ConsumerRecord<K, V>> records = new ArrayList<>();\n\n        try {\n            for (int i = 0; i < maxRecords; i++) {\n                // Only move to next record if there was no exception in the last fetch. Otherwise, we should\n                // use the last record to do deserialization again.\n                if (cachedRecordException == null) {\n                    corruptLastRecord = true;\n                    lastRecord = nextFetchedRecord(fetchConfig);\n                    corruptLastRecord = false;\n                }\n","sourceCodeStart":244,"sourceCodeEnd":280,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/consumer/internals/CompletedFetch.java#L244-L280","documentation":"KafkaException thrown at the top of CompletedFetch.fetchRecords (CompletedFetch.java:262) when a previous iteration set corruptLastRecord=true and cached an exception. It surfaces the earlier error (e.g. CRC failure, decompression error) on the next fetchRecords call because the consumer cannot advance past the offending record, and includes a hint to seek past it. The original exception is preserved as the cause.","triggerScenarios":"After a prior fetchRecords partially failed (nextFetchedRecord or parseRecord threw), corruptLastRecord is left true and cachedRecordException set; on the next poll cycle for that partition, fetchRecords re-enters at the top of CompletedFetch.java:261 and immediately rethrows.","commonSituations":"A poison-pill record the consumer keeps returning because position hasn't advanced; a bad batch returned by the broker that never recovers on re-fetch; transient corruption that hasn't cleared; an unsupported compression type or format mismatch on a specific record.","solutions":["Catch the exception, then consumer.seek(partition, consumer.position(partition) + 1) to skip the poison record and resume polling.","Inspect the offending bytes on the broker with kafka-console-consumer or kafka-dump-log to identify the bad producer.","Wrap the key/value deserializers to return a sentinel instead of throwing so poison pills stop crashing the poll loop.","Confirm producer/consumer message-format and codec compatibility; fix the producer if it is sending malformed records."],"exampleFix":"// before\nwhile (running) {\n    ConsumerRecords<K,V> recs = consumer.poll(Duration.ofSeconds(1));\n    // a poison-pill record throws here and kills the consumer\n}\n\n// after\ntry {\n    ConsumerRecords<K,V> recs = consumer.poll(Duration.ofSeconds(1));\n} catch (KafkaException e) {\n    log.warn(\"Skipping bad record on {}\", consumer.assignment(), e);\n    consumer.assignment().forEach(tp -> consumer.seek(tp, consumer.position(tp) + 1));\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    ConsumerRecords<K, V> recs = consumer.poll(Duration.ofSeconds(1));\n} catch (SerializationException | KafkaException e) {\n    // Deserialization failed on a record; seek past it to resume consumption\n    TopicPartition tp = failingPartition;\n    consumer.seek(tp, consumer.position(tp) + 1);\n}","preventionTips":["Wrap key/value Deserializers to catch per-record errors and return a sentinel instead of throwing.","Use an error-handling deserializer (e.g. Spring ErrorHandlingDeserializer) to deflect poison pills.","Always seek past the offending offset after a deserialization failure; do not get stuck.","Log failing offsets/topic for producer-side root-cause investigation."],"tags":["consumer","poison-pill","records"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}