quarkusio/quarkus · error · IllegalStateException
No Kafka record metadata found on incoming message
Error message
No Kafka record metadata found on incoming message
What it means
The blocking ExactlyOnceInvoker sends the produced result inside a Kafka transaction that must be driven by the record metadata of the incoming message (IncomingKafkaRecordMetadata). When neither record nor batch metadata is present on the incoming message, no Kafka transaction can be created, so the invoker throws IllegalStateException at runtime.
Source
Thrown at extensions/smallrye-reactive-messaging-kafka/runtime/src/main/java/io/quarkus/smallrye/reactivemessaging/kafka/ExactlyOnceInvoker.java:53
Object[] beanArgs = Arrays.copyOf(args, args.length - SYNTHETIC_PARAM_COUNT);
return invokeBeanInTransaction(kafkaTransactions, beanArgs, recordMeta, batchMeta);
}
protected Object invokeBeanInTransaction(KafkaTransactions<Object> tx, Object[] beanArgs,
IncomingKafkaRecordMetadata<?, ?> recordMeta, IncomingKafkaRecordBatchMetadata<?, ?> batchMeta) {
if (batchMeta != null) {
tx.withTransactionAndAwait(batchMeta, emitter -> {
sendResult(invokeBean(beanArgs), emitter);
return Uni.createFrom().voidItem();
});
} else if (recordMeta != null) {
tx.withTransactionAndAwait(recordMeta, emitter -> {
sendResult(invokeBean(beanArgs), emitter);
return Uni.createFrom().voidItem();
});
} else {
throw new IllegalStateException("No Kafka record metadata found on incoming message");
}
return null;
}
protected void sendResult(Object result, TransactionalEmitter<Object> emitter) {
if (result == null) {
return;
}
if (result instanceof Iterable<?> items) {
for (Object item : items) {
emitter.send(item);
}
} else if (result instanceof Multi<?> multi) {
for (Object item : multi.subscribe().asIterable()) {
emitter.send(item);
}
} else {
emitter.send(result);View on GitHub (pinned to e1c734241f)
Solutions
- Ensure messages arriving at the exactly-once method carry IncomingKafkaRecordMetadata (consume directly from the Kafka connector, don't rewrite the Message).
- When transforming, use IncomingKafkaRecord/Message.withMetadataWithFallback to preserve metadata.
- Do not feed Emitters/in-memory channels into @ExactlyOnce consumers.
Example fix
// before Message<String> m = Message.of(payload); // metadata lost // after Message<String> m = message.withMetadataWithFallback(original.getMetadata()); // preserve Kafka metadata
Defensive patterns
Strategy: try-catch
Validate before calling
// guard before invoking exactly-once logic
IncomingKafkaRecordMetadata<?, ?> meta = msg.getMetadata(IncomingKafkaRecordMetadata.class).orElse(null);
if (meta == null && msg.getMetadata(IncomingKafkaRecordBatchMetadata.class).isEmpty()) {
throw new IllegalStateException("Message lacks Kafka metadata; cannot do exactly-once");
} Type guard
Optional<IncomingKafkaRecordMetadata<?, ?>> hasKafkaMeta(Message<?> m) {
return m.getMetadata(IncomingKafkaRecordMetadata.class);
} Try / catch
try {
invoker.invoke(message);
} catch (IllegalStateException e) {
if (e.getMessage().contains("No Kafka record metadata")) {
// nack or route to dead-letter; exactly-once cannot proceed
message.nack(e);
} else throw e;
} Prevention
- Never rewrite Messages with Message.of() when transforming Kafka records; preserve metadata
- Do not feed Emitters/in-memory channels into @ExactlyOnce consumers
- Add a unit test asserting IncomingKafkaRecordMetadata presence on messages
When it happens
Trigger: invoke() reaches invokeBeanInTransaction with a Message lacking IncomingKafkaRecordMetadata and IncomingKafkaRecordBatchMetadata — typically a plain Message created by application code or bridged from another connector into an exactly-once channel.
Common situations: Emitting plain Message payloads via an Emitter into a channel consumed by an @ExactlyOnce method, or transforming messages and dropping Kafka metadata (e.g. Message.of(payload) mapping).
Related errors
- No Kafka record metadata found on incoming message
- @ExactlyOnce on method ${methodName} requires both @Incoming
- @ExactlyOnce on method ${methodName} must return a value to
- @ExactlyOnce on method ${methodName} cannot be combined with
- @ExactlyOnce on method ${methodName} cannot combine @WithTra
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/9c64a887d847ce24.
Report an issue: GitHub.