Federico Mariani created CAMEL-24138:
----------------------------------------
Summary: camel-core - Splitter errorThreshold/maxFailedRecords:
failure tracker leaks into nested splits and silently swallows their failures
Key: CAMEL-24138
URL: https://issues.apache.org/jira/browse/CAMEL-24138
Project: Camel
Issue Type: Bug
Components: camel-core, eip
Affects Versions: 4.22.0
Reporter: Federico Mariani
The new {{errorThreshold}}/{{maxFailedRecords}} options (CAMEL-23264,
unreleased 4.22.0-SNAPSHOT) store the splitter's {{SplitFailureTracker}} as a
plain exchange property ({{CamelSplitFailureTracker}}, {{Splitter.java:210}}).
Exchange properties are copied into every sub-exchange by
{{createCorrelatedCopy}}, so the tracker *leaks into any nested splitter*
running inside the outer split. The inner splitter's
{{shouldContinueOnFailure}} looks the tracker up on _its_ input exchange:
{code:java}
SplitFailureTracker tracker = original.getProperty(SPLIT_FAILURE_TRACKER,
SplitFailureTracker.class);
if (tracker == null) {
return super.shouldContinueOnFailure(subExchange, original, index); //
normal behavior
}
tracker.recordFailure(index, subExchange.getException());
// the inner splitter has maxFailedRecords == 0 and errorThreshold == 0, so
both checks are skipped
...
subExchange.setException(null); // <-- inner failure silently cleared
return true;
{code}
Because the _inner_ splitter instance has no thresholds configured, both
threshold checks are skipped and it unconditionally clears the exception of its
own failed sub-items. Consequences:
# Inner split failures no longer propagate — the inner split (and therefore the
outer item) *succeeds silently* even though items failed. The outer
{{maxFailedRecords}}/{{errorThreshold}} never trips.
# Inner failures are recorded into the *outer* tracker with *inner* indexes,
corrupting {{SplitResult}} ({{failureCount}} can exceed {{totalItems}}, making
{{getSuccessCount()}} negative).
*Reproducer (attached, fails on main):*
{{SplitterNestedFailureTrackerLeakIssueTest}} — outer
{{split(body()).maxFailedRecords(1)}} around a plain inner {{split(body())}}
whose item throws; the exchange completes without an exception instead of
aborting:
{noformat}
org.opentest4j.AssertionFailedError: Inner split failure was silently swallowed
by the leaked outer failure tracker ==> expected: not <null>
{noformat}
Suggested fix direction: don't discover the tracker via an inherited exchange
property — key it per-processor instance (like the {{AGGREGATION_STRATEGY}} map
keyed by processor), or only honor the tracker when {{this}} splitter actually
has thresholds configured ({{errorThreshold > 0 || maxFailedRecords > 0}}).
Since CAMEL-23264 is unreleased, this can still be fixed before 4.22.0 ships.
_Filed by Claude Code on behalf of Federico Mariani (fmariani)_
--
This message was sent by Atlassian Jira
(v8.20.10#820010)