{"record":{"id":"350148f847231a10","repo":"apache/rocketmq","slug":"send-message-exception","errorCode":null,"errorMessage":"send message Exception","messagePattern":"send message Exception","errorType":"exception","errorClass":"MQClientException","httpStatus":null,"severity":"critical","filePath":"client/src/main/java/org/apache/rocketmq/client/impl/producer/DefaultMQProducerImpl.java","lineNumber":1450,"sourceCode":"\n    public TransactionSendResult sendMessageInTransaction(final Message msg,\n        final TransactionListener localTransactionListener, final Object arg)\n        throws MQClientException {\n        TransactionListener transactionListener = getCheckListener();\n        if (null == localTransactionListener && null == transactionListener) {\n            throw new MQClientException(\"tranExecutor is null\", null);\n        }\n\n        ensureNotDelayedForTransactional(msg);\n        Validators.checkMessage(msg, this.defaultMQProducer);\n\n        SendResult sendResult = null;\n        MessageAccessor.putProperty(msg, MessageConst.PROPERTY_TRANSACTION_PREPARED, \"true\");\n        MessageAccessor.putProperty(msg, MessageConst.PROPERTY_PRODUCER_GROUP, this.defaultMQProducer.getProducerGroup());\n        try {\n            sendResult = this.send(msg);\n        } catch (Exception e) {\n            throw new MQClientException(\"send message Exception\", e);\n        }\n\n        LocalTransactionState localTransactionState = LocalTransactionState.UNKNOW;\n        Throwable localException = null;\n        switch (sendResult.getSendStatus()) {\n            case SEND_OK: {\n                try {\n                    if (sendResult.getTransactionId() != null) {\n                        msg.putUserProperty(\"__transactionId__\", sendResult.getTransactionId());\n                    }\n                    String transactionId = msg.getProperty(MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX);\n                    if (null != transactionId && !\"\".equals(transactionId)) {\n                        msg.setTransactionId(transactionId);\n                    }\n                    if (null != localTransactionListener) {\n                        localTransactionState = localTransactionListener.executeLocalTransaction(msg, arg);\n                    } else {\n                        log.debug(\"Used new transaction API\");","sourceCodeStart":1432,"sourceCodeEnd":1468,"githubUrl":"https://github.com/apache/rocketmq/blob/293f5885719fc4aa3619446a1900f58ccfcfdd29/client/src/main/java/org/apache/rocketmq/client/impl/producer/DefaultMQProducerImpl.java#L1432-L1468","documentation":"Thrown by sendMessageInTransaction when the initial synchronous send of the half message (this.send(msg) with PROPERTY_TRANSACTION_PREPARED set) raises any Exception. Without a successfully delivered half message there is no transaction to execute, so the client surfaces the underlying send failure wrapped in MQClientException('send message Exception', cause).","triggerScenarios":"producer.sendMessageInTransaction(...) where the half-message send fails: no route for the topic, broker rejection, remoting timeout, InterruptedException during shutdown, or message validation failure.","commonSituations":"Transactional topic not created / no route (most common on fresh clusters); broker down or busy at the moment of the half send; sending during producer shutdown (interrupt); message body violating broker limits.","solutions":["Inspect the nested cause: MQClientException with 'No route info' -> create/verify the topic; RemotingTimeoutException -> check broker health/latency; MQBrokerException -> act on response code.","Verify the topic exists and the producing permission is set before entering the transactional flow (fetchPublishMessageQueues as a readiness probe).","Keep the local transaction unstarted until sendMessageInTransaction returns, so a failed half send does not orphan a local DB transaction.","Retry the whole sendMessageInTransaction call idempotently for transient network causes."],"exampleFix":"// before\ntry {\n    producer.sendMessageInTransaction(msg, listener, arg);\n} catch (MQClientException e) {\n    // local DB tx already committed -> inconsistent!\n}\n\n// after\n// start nothing locally beforehand; RocketMQ drives the local branch via listener\nTransactionSendResult r = producer.sendMessageInTransaction(msg, listener, arg);\nif (r.getLocalTransactionState() != LocalTransactionState.COMMIT_MESSAGE) {\n    metrics.txAborted(arg);\n}","handlingStrategy":"retry","validationCode":"// probe route before entering transactional flow\nif (producer.fetchPublishMessageQueues(msg.getTopic()).isEmpty()) {\n    throw new IllegalStateException(\"topic \" + msg.getTopic() + \" has no route; half message would fail\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    TransactionSendResult r = producer.sendMessageInTransaction(msg, listener, arg);\n} catch (MQClientException e) {\n    if (e.getMessage().startsWith(\"send message Exception\")) {\n        Throwable c = e.getCause();\n        if (isTransient(c)) retryTransactionally(msg, listener, arg); // half send never landed\n        else throw new IllegalStateException(\"half message send failed\", c);\n    } else throw e;\n}","preventionTips":["Start local DB work only inside the TransactionListener, never before sendMessageInTransaction.","Pre-verify the transactional topic's route and permissions during deployment checks.","Make half-message retries idempotent (unique business key) since a timeout may have actually delivered."],"tags":["rocketmq","producer","transactional-message","half-message","send-failure"],"backgroundTag":null,"analyzedSha":"293f5885719fc4aa3619446a1900f58ccfcfdd29","analyzedAt":"2026-08-14T11:50:13.822Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}