alibaba/spring-cloud-alibaba · error · MessagingException
TransactionMQProducer must have a TransactionListener !!!
Error message
TransactionMQProducer must have a TransactionListener !!!
What it means
In handleMessageInternal, when the producer is a TransactionMQProducer the handler looks up a TransactionListener bean by name (`mqProducerProperties.getTransactionListener()`) via RocketMQBeanContainerCache. If none resolves, it throws MessagingException - transactional sends require a listener to drive local-execute and check-back.
Source
Thrown at spring-cloud-alibaba-starters/spring-cloud-starter-stream-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/integration/outbound/RocketMQProducerMessageHandler.java:176
}
@Override
public boolean isRunning() {
return running;
}
@Override
protected void handleMessageInternal(Message<?> message) {
try {
org.apache.rocketmq.common.message.Message mqMessage = RocketMQMessageConverterSupport
.convertMessage2MQ(destination.getName(), message);
SendResult sendResult;
if (defaultMQProducer instanceof TransactionMQProducer translateMQProducer) {
TransactionListener transactionListener = RocketMQBeanContainerCache
.getBean(mqProducerProperties.getTransactionListener(),
TransactionListener.class);
if (transactionListener == null) {
throw new MessagingException(
"TransactionMQProducer must have a TransactionListener !!! ");
}
translateMQProducer.setTransactionListener(transactionListener);
if (log.isDebugEnabled()) {
log.debug("send transaction message ->{}", mqMessage);
}
sendResult = defaultMQProducer.sendMessageInTransaction(mqMessage,
message.getHeaders().get(RocketMQConst.USER_TRANSACTIONAL_ARGS));
}
else {
if (log.isDebugEnabled()) {
log.debug("send message ->{}", mqMessage);
}
sendResult = this.send(mqMessage, this.messageQueueSelector,
message.getHeaders(), message);
}
if (log.isDebugEnabled()) {
log.debug("the message has sent,message={},sendResult={}", mqMessage,View on GitHub (pinned to 115d590110)
Solutions
- Register a TransactionListener bean and reference its name via `spring.cloud.stream.rocketmq.bindings.<output>.producer.transactionListener=<beanName>`.
- If you don't need transactions, set `producerType: Normal` (default).
- Verify the bean is visible to RocketMQBeanContainerCache (registered in the application context).
Example fix
// before
spring:
cloud:
stream:
rocketmq:
bindings:
txOut:
producer:
producerType: Trans # no transactionListener -> [131]
// after
@Bean("myTxListener")
TransactionListener myTxListener() { /* ... */ return /* ... */; }
// yaml:
spring:
cloud:
stream:
rocketmq:
bindings:
txOut:
producer:
producerType: Trans
transactionListener: myTxListener Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight: transactional producers must resolve a listener.
if (RocketMQProducerProperties.ProducerType.Trans.equalsName(mqProducerProperties.getProducerType())) {
TransactionListener tl = RocketMQBeanContainerCache
.getBean(mqProducerProperties.getTransactionListener(), TransactionListener.class);
Assert.notNull(tl, "transactionListener bean required for producerType=Trans (avoids [131])");
} Type guard
// Type guard narrowing the producer type before send.
boolean transactional = defaultMQProducer instanceof TransactionMQProducer;
boolean hasListener = RocketMQBeanContainerCache
.getBean(mqProducerProperties.getTransactionListener(), TransactionListener.class) != null;
if (transactional && !hasListener) {
throw new IllegalStateException("Trans producer requires a TransactionListener bean");
} Prevention
- Pair every `producerType: Trans` with a named TransactionListener bean.
- Default to producerType Normal unless you need transactions.
- Confirm bean visibility to RocketMQBeanContainerCache.
When it happens
Trigger: producerType is set to Trans (so a TransactionMQProducer is built) but no TransactionListener bean is registered/resolvable under the configured `transactionListener` name.
Common situations: Set `producerType: Trans` without providing a `transactionListener` bean; bean name typo; the listener bean lives in a context RocketMQBeanContainerCache does not scan; a transaction example copied incompletely.
Related errors
- Binding for channel {destination.getName()} has been disable
- message hasn't been sent,cause by : the SendType must be in
- empty after conversion [messageConverter:%s,payloadClass:%s,
- topic must not be empty
- group must be configured for DLQ{destination.getName()}
AI-assisted analysis of alibaba/spring-cloud-alibaba@115d590110 (2026-08-14).
Data as JSON: /api/errors/eb3f53b154753edc.
Report an issue: GitHub.