shashank created CAMEL-26332:
--------------------------------

             Summary: camel-core - doTry: after a route called from the doTry 
body continued a failure (onException continued), no doCatch of that doTry 
catches a later exception (regression in 4.23.0)
                 Key: CAMEL-26332
                 URL: https://issues.apache.org/jira/browse/CAMEL-26332
             Project: Camel
          Issue Type: Bug
          Components: camel-core
            Reporter: shashank
            Assignee: shashank


After a route called from a {{doTry}} body continued a failure 
({{onException(...).continued(true)}}), no {{doCatch}} of that {{doTry}} 
catches a later exception in the same {{doTry}} body. The exception escapes the 
{{doTry}}, and the exchange fails where the route has a handler (main 
8e0ce2d21371 and 4.23.0; 4.22.1 catches):
{code:java}
onException(NumberFormatException.class).continued(true);

from("direct:start")
    .doTry()
        .to("direct:callee")                                  // fails, the 
onException continues it
        .to("mock:next")                                      // runs
        .throwException(new IllegalStateException("after the call"))
    .doCatch(IllegalStateException.class)
        .to("mock:catch")                                     // never runs
    .end();
from("direct:callee").throwException(new NumberFormatException("in 
callee")).to("mock:calleeEnd");

template.send("direct:start", ...)   ->  mock:catch 0, exception 
IllegalStateException("after the call")
{code}
The same happens when the callee is called with a routing slip, a dynamic 
router or a recipient list, and when the onException redelivers before it 
continues. Without the call before the exception, or when the called route does 
not fail, the {{doCatch}} catches. The docs say that {{continued}} handles and 
continues routing "as if the exception did not occur", and that the first 
{{doCatch}} that matches is used.

It is a regression of CAMEL-25115 (4.23.0). To keep a continued exception from 
being added as a suppressed exception of a later failure, 
{{RedeliveryErrorHandler}} now stores the continued exception in the exchange 
property {{CamelExceptionHandled}} ({{Exchange.EXCEPTION_HANDLED}}). 
{{CatchProcessor}} uses the same property to skip the other {{doCatch}} blocks 
once one of them handled the exception, and it skips the clause when the 
property has any value. {{TryProcessor}} clears the property only when the 
{{doTry}} starts, and a {{direct:}} call shares the exchange, so the continued 
failure in the called route disables every {{doCatch}} of the caller's 
{{doTry}}.

*Fix* (proposed): {{RedeliveryErrorHandler}} keeps the continued exception 
under an exchange property of its own (a private constant), and the check of 
CAMEL-25115 compares with that property. {{CamelExceptionHandled}} is then 
again set only by a {{doCatch}} (and by a circuit breaker fallback), as in 
4.22; {{CatchProcessor}}, {{TryProcessor}} and 
{{CatchDefinition.acceptDebugger}} are not changed. This also closes a gap in 
the CAMEL-25115 check: a {{doTry}} removed {{CamelExceptionHandled}} when it 
started, so a failure continued before a {{doTry}} was still added as a 
suppressed exception to a failure inside the {{doTry}}. A smaller change 
({{CatchProcessor}} skips the clause only when the property is 
{{Boolean.TRUE}}) is not enough: the error handler overwrites the {{true}} of a 
{{doCatch}} that calls a route which continues a failure, and an exception that 
this {{doCatch}} then throws would be caught by the next {{doCatch}} of the 
same {{doTry}}. No upgrade guide entry: the {{doCatch}} catches again as in 
4.22.

*Tests*: {{DoCatchAfterContinuedFailureInCalledRouteTest}} (9 tests: a 
continued failure in a route called with {{to}}, a routing slip, a recipient 
list and a dynamic router, a continued failure after a redelivery, and a 
failure continued before the {{doTry}} that must not become a suppressed 
exception; 6 fail on main and pass with the fix. Three controls pass on main as 
well: nothing before the exception, a called route that does not fail, and an 
exception thrown by a {{doCatch}} that is not caught by the next {{doCatch}}). 
The doTry/doCatch/doFinally, onException, redelivery, dead letter channel, 
error handler, routing slip, recipient list, dynamic router and pipeline tests 
of camel-core pass (881 tests); the whole camel-core suite has only the known 
failures of the local offline build.

*Not changed*: the circuit breaker fallbacks (camel-resilience4j, 
camel-microprofile-fault-tolerance) set {{CamelExceptionHandled}} to {{true}} 
like a {{doCatch}}, so after a fallback ran inside a {{doTry}} body a later 
exception in that body is not caught either. That is older than CAMEL-25115 
(found by reading the code, not tested) and can be a separate issue.

*How it was found*: a Rocq specification of Camel's error handling, written 
from the documentation, was compared with real Camel on generated routes, then 
reproduced on HotSpot.

*Affected*: main and 4.23.0 only. camel-4.22.x, 4.18.x and 4.14.x are not 
affected: there, an exception is stored in that property only by the error 
handler task used without {{onException}} and without redelivery, which never 
continues an exchange (4.22.1 catches).

_Filed with Claude Code on behalf of allthingssecurity._



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

Reply via email to