shashank created CAMEL-26447:
--------------------------------

             Summary: camel-core - doTry: after a doCatch caught the failure of 
a called route that failed unhandled, the later error handlers of the exchange 
treat it as done
                 Key: CAMEL-26447
                 URL: https://issues.apache.org/jira/browse/CAMEL-26447
             Project: Camel
          Issue Type: Bug
          Components: camel-core
            Reporter: shashank
            Assignee: shashank


Follow-up of CAMEL-26434 (apache/camel#28786), named in its review. Related to 
CAMEL-26333.

When the {{doTry}} block calls a route (such as with {{direct}}) that fails 
*unhandled* (default error handler, no {{onException}}), that route's error 
handler marks the exchange as failure handled ({{failureHandled=true}}, 
{{errorHandlerHandled=false}}). CAMEL-26434 clears the mark while the 
{{doCatch}} block runs, but sets it again after the block, as before. The mark 
then stays on the exchange for the rest of the calling route, so the error 
handlers that come later treat the exchange as done 
({{RedeliveryErrorHandler.isDone}}):

{code:java}
// a later failure in the calling route, after the doTry
from("direct:laterFailure")
    .onException(IOException.class).handled(true).to("mock:callerHandled").end()
    .doTry().to("direct:failing")
    .doCatch(IllegalArgumentException.class).to("mock:catch")
    .end()
    .throwException(new IOException("after the doTry"));

// a route called from the doFinally after such a doCatch
from("direct:finallyAfterCatch")
    .doTry().to("direct:failing")
    .doCatch(IllegalArgumentException.class).to("mock:catch")
    .doFinally().to("direct:dlcRoute")
    .end();

// the doCatch throws, inside a route called by a route with a dead letter 
channel
from("direct:outer").errorHandler(deadLetterChannel("mock:outerDead").redeliveryDelay(0))
    .to("direct:inner");
from("direct:inner")
    .doTry().to("direct:failing")
    .doCatch(IllegalArgumentException.class).throwException(new 
IllegalStateException("in catch"))
    .end();

from("direct:failing").throwException(new IllegalArgumentException("callee"));
from("direct:dlcRoute").errorHandler(deadLetterChannel("mock:dead").redeliveryDelay(0))
    .throwException(new IOException("in finally"));
{code}

On main plus CAMEL-26434 (the result is the same on main), with the same routes 
where the {{doTry}} throws in the route itself as controls:

||case||doTry fails in a called route||doTry fails in the route (control)||
|later failure in the calling route|{{IOException}}, onException not 
run|handled by onException|
|route called from the doFinally|{{IOException: in finally}}, mock:dead 
0|mock:dead 1|
|doCatch throws, outer route with a DLC|{{IllegalStateException: in catch}}, 
mock:outerDead 0|mock:outerDead 1|

A route called later with a plain {{to("direct:x")}} cannot handle its failure 
either, while through a copy without the mark ({{enrich}}, {{split}}) it can.

*Proposal*
* Clear the failure handled mark for good when a {{doCatch}} catches the 
exception, as for a failure in the route itself.
* This changes what the readers of the mark see at the end of the exchange: 
{{IdempotentOnCompletion}} confirms the message instead of removing it, 
{{ResumableCompletion}} records the offset, and the JMX / Micrometer / 
OpenTelemetry {{failuresHandled}} counters no longer count it. So it needs an 
upgrade guide entry and a way back; to be designed with the fix.
* Tests: the three cases above with their in-route controls, plus a plain 
{{to}} after the doTry.

_Filed with Claude Code on behalf of allthingssecurity._



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to