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)