[
https://issues.apache.org/jira/browse/CAMEL-25695?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Serdar Gökay resolved CAMEL-25695.
----------------------------------
Fix Version/s: 4.24.0
Resolution: Fixed
Fixed by the CAMEL-25694 change: apache/camel#27956 (merged 2026-10-23, commit
33075400207) replaces handleAsync() with whenCompleteAsync() in
SubscriptionManager.write(), call(), readValues() and browse(), so an
exceptional completion of readValues() now reaches MiloClientProducer and fails
the exchange instead of completing with a null body.
SubscriptionManagerExceptionPropagationTest covers the readValues() path.
> camel-milo - awaited readValues service failures are reported as success
> ------------------------------------------------------------------------
>
> Key: CAMEL-25695
> URL: https://issues.apache.org/jira/browse/CAMEL-25695
> Project: Camel
> Issue Type: Bug
> Components: camel-milo
> Affects Versions: 4.22.1
> Environment: Camel 4.22.1; Eclipse Milo 1.1.6; OpenJDK 25.0.4; Maven
> 3.9.16; macOS arm64. Attached reproduction uses a real loopback Milo OPC UA
> server. Independent network confirmation used asyncua 1.1.8 on Python 3.12
> and a loopback TCP relay. No physical PLC or production deployment.
> Reporter: Serdar Gökay
> Priority: Minor
> Fix For: 4.24.0
>
> Attachments: camel-milo-readvalues-failure-reproducer.zip
>
>
> h3. Problem
> An awaited producer read selected by {{CamelMiloNodeIds}} can fail at the OPC
> UA service/transport layer while the Camel exchange completes successfully
> with a null body. The success continuation runs and configured redelivery,
> {{onException}} and dead-letter handling are skipped.
> Reproduced on released Camel 4.22.1 / Eclipse Milo 1.1.6. Both
> {{CamelMiloAwait=true}} and {{defaultAwaitWrites=true}} reproduce the issue.
> This concerns producer reads, not subscription-consumer delivery. Related
> write/call finding: CAMEL-25694.
> h3. Reproduction
> The attached standalone Maven project starts its own real loopback TCP OPC UA
> server. It does not mock the client, futures or transport, and does not patch
> Camel classes.
> {code:bash}
> mvn -B -ntp -Duser.language=en -Duser.country=US
> -Dtest=SingleNodeReadContractTest test
> mvn -B -ntp -Duser.language=en -Duser.country=US
> -Dtest=AwaitedReadContractTest test
> {code}
> # Establish a connection and successfully read the test node(s); verify
> values and Good statuses.
> # Make a subsequent awaited read. The real server returns a {{Bad_Timeout}}
> ServiceFault for that read.
> # Inspect the exchange and route counters:
> {noformat}
> READ_CONTRACT_RESULT camel=4.22.1 milo=1.1.6 mode=BAD_TIMEOUT
> reads=1 redeliveries=0 downstream=1 failed=false exception=null body=null
> {noformat}
> Both desired-contract tests fail specifically because the exchange exception
> is missing. The minimal reproduction needs only one node. A direct Milo SDK
> client against the same server/fault throws {{UaException}} with the matching
> status.
> The attachment also contains 14 characterization/control tests, run with
> {{mvn test '-Dtest=*EvidenceTest'}}. These pass by asserting the observed
> behavior, including the defect; they do not demonstrate a fix. They cover
> both await settings, {{Bad_Timeout}} and {{Bad_SessionClosed}} service
> responses, native SDK controls, successful list types/order/duplicates,
> namespace-URI addressing, per-node bad status, retry/DLQ/onException controls
> and explicitly non-awaited behavior.
> Independent network testing used a normal asyncua OPC UA server behind a
> loopback TCP relay. Dropping requests, dropping responses or closing the
> established connection produced the same null-success result. Nine network
> cases were run twice: four healthy controls passed and all fourteen failure
> executions failed the desired contract. These network scripts are separate
> from the attached minimal Maven reproduction.
> h3. Expected behavior and scope
> When an awaited read completes exceptionally, preserve that exceptional
> completion so {{MiloClientProducer}} can fail the exchange and configured
> Camel error handling can run. Preserve successful {{List<DataValue>}} results
> and ordinary per-node bad statuses, such as {{Bad_NodeIdUnknown}}, inside
> their individual {{DataValue}} results.
> An already-disconnected manager has a separate exceptional {{No connected}}
> path; the reproduction deliberately establishes a healthy connection first.
> Explicit non-await mode is not the awaited defect. {{Bad_SessionClosed}} is a
> controlled service-error response, not an actual session-expiry simulation.
> No physical PLC or subscription behavior is claimed.
> h3. Cause and proposed direction
> In outer {{SubscriptionManager.readValues}}, {{handleAsync}} calls
> {{handleConnectionFailure(error)}} and then returns {{nodes}}, which is null
> after exceptional completion. That converts a failed future into normal
> completion. The producer's existing awaited callback consequently sees no
> throwable.
> Proposed change, keeping the executor and recovery callback:
> {code:java}
> return this.connected.readValues(nodeIds).whenCompleteAsync((nodes, error) ->
> {
> if (error != null) {
> handleConnectionFailure(error);
> }
> }, this.executor);
> {code}
> This is a proposed direction, not an applied or verified fix. The completion
> observer preserves both successful lists and exceptional completion.
> Pinned released source:
> [SubscriptionManager.readValues|https://github.com/apache/camel/blob/c056bef67cdfeffdc0ad7255e5e052cab71f2921/components/camel-milo/src/main/java/org/apache/camel/component/milo/client/internal/SubscriptionManager.java#L1014-L1032]
> and
> [MiloClientProducer|https://github.com/apache/camel/blob/c056bef67cdfeffdc0ad7255e5e052cab71f2921/components/camel-milo/src/main/java/org/apache/camel/component/milo/client/MiloClientProducer.java#L77-L103].
> _This report and reproduction are AI-generated with Codex on behalf of GitHub
> user etzel-mes._
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)