shashank created CAMEL-26741:
--------------------------------
Summary: camel-core - an Error thrown by a plain Processor escapes
after the callback: the route continues as if it succeeded and the exchange
stays inflight
Key: CAMEL-26741
URL: https://issues.apache.org/jira/browse/CAMEL-26741
Project: Camel
Issue Type: Bug
Components: camel-core
Reporter: shashank
Assignee: shashank
A plain {{Processor}} is called as an {{AsyncProcessor}} through an adapter:
{{DelegateSyncProcessor}} for {{process(...)}}, the bridge of
{{AsyncProcessorConverterHelper}} (for example a producer that is not an
{{AsyncProcessor}}), and the Processor adapter of the bean component
({{AbstractBeanProcessor.useCustomAdapter}}, for a bean that is a
{{Processor}}). Each calls the callback in {{finally}}, but catches only
exceptions ({{DelegateSyncProcessor}} also {{LinkageError}}, the bean adapter
also {{AssertionError}}). Any other {{Error}}, such as an {{AssertionError}} in
a test processor or a custom {{Error}}, is thrown on after the callback was
called:
* the exchange has no exception, so the callback continues it and the next
steps run as if the processor had succeeded;
* the error handler, {{onException}} and {{doCatch}} never see the {{Error}};
* the {{Error}} leaves the reactive executor's loop, so work already queued
there is left behind: the exchange stays inflight, the context stop waits its
whole shutdown timeout, and the queued work may run later on another exchange's
send.
{code:java}
from("direct:start")
.onException(Error.class).handled(true).to("mock:handled").end()
.process(e -> { throw new Error("boom"); })
.to("mock:result");
{code}
On main, without the {{onException}}, {{template.send("direct:start", ...)}}
throws the {{Error}} to the caller and the exchange stays inflight; with it,
{{mock:handled}} receives nothing and {{mock:result}} receives the message.
Stopping the context with a 5 second shutdown timeout then took 5 s.
Camel already treats an {{Error}} of a step as a failure elsewhere: the error
handlers and {{CamelInternalProcessor}} catch {{Throwable}} when a processor
throws before calling its callback (CAMEL-26464),
{{DefaultErrorHandlerCatchThrowableTest}} expects a {{NoSuchMethodError}} of a
processor to fail the exchange, CAMEL-16821 made the bean adapter fail the
exchange on an {{AssertionError}}, and CAMEL-19497 moved its callback into
{{finally}} so that "unhandled exceptions" do not block. CAMEL-19748 narrowed
these catches from {{Throwable}}, as catching JVM errors can be dangerous; here
the {{Error}} is not passed on cleanly either, as the callback was already
called.
Seen while building the exchange sanitizer (CAMEL-26714): an {{Error}} of a
plain {{Processor}} breaks the rule "call the callback exactly once, and do not
throw from process" in Camel's adapter, not in the processor.
Suggested fix: the three adapters catch {{Throwable}} and set it on the
exchange (an {{Error}} is wrapped in a {{CamelExecutionException}} by
{{setException}}), so the exchange fails and error handling applies, as for an
exception. This changes behaviour for an {{Error}}, so it needs an upgrade
guide note.
_Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)