shashank created CAMEL-25272:
--------------------------------

             Summary: camel-rocketmq - the consumer acknowledges the message of 
a failed exchange (the message is lost), and an InOut send without replyToTopic 
completes successfully when the send fails
                 Key: CAMEL-25272
                 URL: https://issues.apache.org/jira/browse/CAMEL-25272
             Project: Camel
          Issue Type: Bug
          Components: camel-rocketmq
            Reporter: shashank


Two defects in camel-rocketmq (code unchanged since the component was added in 
CAMEL-14831).

h3. 1. The consumer acknowledges the message of a failed exchange

{{RocketMQConsumer}} registers a {{MessageListenerConcurrently}} whose status 
acknowledges the message:

{code:java}
try {
    getProcessor().process(exchange);
} catch (Exception e) {
    getExceptionHandler().handleException(e);
    return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
{code}

A route that fails does not throw from {{process}}: the error handler sets the 
exception on the exchange. So the listener answers {{CONSUME_SUCCESS}} for 
every failed exchange, the broker takes the message as consumed, and it is 
never redelivered (no retry topic, no dead letter queue). Any failure of the 
route (a database down, a validation error) loses the message, which is what 
the push consumer's {{RECONSUME_LATER}} is there to prevent.

h3. 2. An InOut send without replyToTopic completes successfully when the send 
fails

{{RocketMQProducer.processInOut}} is used for every exchange whose pattern is 
out capable, for example a message coming from an HTTP consumer. Without 
{{replyToTopic}} there is no reply manager ({{replyManager}} is {{null}}), and 
the send callback does:

{code:java}
public void onException(Throwable e) {
    try {
        replyManager.cancelMessageKey(generateKey);   // NullPointerException
        exchange.setException(e);
    } finally {
        callback.done(false);
    }
}
{code}

The {{NullPointerException}} skips {{setException}}, the {{finally}} completes 
the exchange, and the exchange continues as if the message had been sent. (The 
NPE ends up in the client's async send thread.)

h3. Reproduction (unit tests, no broker)

* {{RocketMQConsumerFailureTest}}: the listener (made a package-private method 
{{consumeMessage}} so that it can be called without a broker) with a processor 
that sends the exchange to a route that throws: main answers 
{{CONSUME_SUCCESS}}, expected {{RECONSUME_LATER}}. Controls: a processor that 
throws, and a successful route ({{CONSUME_SUCCESS}}).
* {{RocketMQProducerSendFailureTest}}: an empty body, which the RocketMQ client 
rejects in its asynchronous send ({{MQClientException}}, "the message body 
length is zero") before it contacts the name server; any asynchronous send 
failure takes the same path. InOnly: the exchange fails (control). InOut 
without {{replyToTopic}}: on main the exchange has no exception.

h3. Proposed fix

* Consumer: set a thrown exception on the exchange, and answer 
{{RECONSUME_LATER}} (after reporting to the exception handler) whenever the 
exchange has an exception; {{CONSUME_SUCCESS}} otherwise.
* Producer: set the exception first, and cancel the message key only when there 
is a reply manager.

With the fix the 5 new tests pass; without it 2 fail. The module's other tests 
are ITs that need Docker (not run). Note that the module skips tests on aarch64 
({{skipTests.aarch64}}, the RocketMQ container is x86 only), so these unit 
tests run on the x86 CI only; I ran them locally with 
{{-DskipTests.aarch64=false -DskipTests=false}}. The upgrade guide for 4.23 
gets a note: messages of failed exchanges are now redelivered (with an 
increasing delay, up to the max reconsume times of the group, 16 by default, 
then the dead letter queue).

Affected: 4.14.x, 4.18.x and main (same code).

Duplicate check (2026-10-02): JIRA text "rocketmq" (12 issues): only the 
component, upgrades and test issues (CAMEL-14831, 19832, 19610, 19684, 20368 
batching consumers, ...). GitHub pull requests "rocketmq": none about the 
consumer status or the producer callback (open #22358 touches only the reply 
manager's {{getOut}}).

_Filed with Claude Code on behalf of allthingssecurity._




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

Reply via email to