shashank created CAMEL-26434:
--------------------------------
Summary: camel-core - doTry: after a route called from the doTry
failed unhandled, a route called from the doCatch cannot handle its own failure
with its error handler
Key: CAMEL-26434
URL: https://issues.apache.org/jira/browse/CAMEL-26434
Project: Camel
Issue Type: Bug
Components: camel-core
Reporter: shashank
Assignee: shashank
Found while working on CAMEL-26333 (apache/camel#28756), from a question in its
review.
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
({{ExchangeExtension.isFailureHandled() == true}},
{{errorHandlerHandled=false}}) before the exception goes back to the caller.
The {{doCatch}} then catches the exception, but {{CatchProcessor}} only clears
the exception, not that mark. A route called from the {{doCatch}} that fails
can then not apply its own error handler: {{RedeliveryErrorHandler.isDone}}
treats an exchange that is failure handled as done, so neither its dead letter
channel nor its handled {{onException}} run, and the exchange fails with that
route's exception.
When the {{doTry}} block fails in the same route (no error handler involved, as
doTry children are not wrapped in the route error handler), the same route
called from the {{doCatch}} handles its failure as expected.
{code:java}
// the doTry calls a route that fails unhandled
from("direct:viaCallee")
.doTry().to("direct:failing")
.doCatch(IllegalArgumentException.class).to("mock:catch").to("direct:dlcRoute")
.end()
.to("mock:after");
// control: the doTry fails in the same route
from("direct:inRoute")
.doTry().throwException(new IllegalArgumentException("in route"))
.doCatch(IllegalArgumentException.class).to("mock:catch").to("direct:dlcRoute")
.end()
.to("mock:after");
from("direct:failing").throwException(new IllegalArgumentException("callee"));
from("direct:dlcRoute").errorHandler(deadLetterChannel("mock:dead").redeliveryDelay(0))
.throwException(new IOException("in catch"));
// direct:viaCalleeOnException and direct:inRouteOnException: as above, with
.to("direct:onExceptionRoute") in the doCatch
from("direct:onExceptionRoute")
.onException(IOException.class).handled(true).to("mock:handled").end()
.throwException(new IOException("in catch"));
{code}
On main (baaf532, 2026-10-31), with a JUnit test ({{ContextTestSupport}}, the
two failing cases failed on all three surefire runs):
||send||doCatch calls||exception of the exchange||mock:dead||mock:handled||
|direct:viaCallee|dead letter channel route|{{IOException: in catch}}|0|0|
|direct:inRoute (control)|dead letter channel route|none|1|0|
|direct:viaCalleeOnException|handled onException route|{{IOException: in
catch}}|0|0|
|direct:inRouteOnException (control)|handled onException route|none|0|1|
In the two failing cases the exchange is {{failureHandled=true}},
{{errorHandlerHandled=false}} when it comes back.
Expected: a route called from a {{doCatch}} handles (or continues) its own
failure with its error handler, whether the {{doTry}} failed in the same route
or in a route it called.
*Proposal*
* Clear the failure handled mark (and {{errorHandlerHandled}}, if set) when the
{{doCatch}} takes over the exception in {{CatchProcessor}}, as CAMEL-26333 does
around the {{doFinally}} block in {{FinallyProcessor}}, and check that the
caller still behaves as before after the {{doCatch}} (it continues after the
{{doTry}}, as the exception is caught). The related history is CAMEL-2577 and
CAMEL-7275 (no nested error handlers for the children of doTry), which this
does not change: the routes called from the {{doCatch}} have error handlers of
their own.
* Tests: the four cases above, plus a nested {{doTry}} and an asynchronous
callee.
_Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)