shashank created CAMEL-25250:
--------------------------------

             Summary: camel-grpc - with the AGGREGATION or PROPAGATION consumer 
strategy a failed exchange is answered with its message body as a successful 
response instead of an error
                 Key: CAMEL-25250
                 URL: https://issues.apache.org/jira/browse/CAMEL-25250
             Project: Camel
          Issue Type: Bug
          Components: camel-grpc
            Reporter: shashank


For unary and server streaming calls, {{GrpcMethodHandler.handle}} ends the 
call with {{Status.INTERNAL}} when the exchange failed (CAMEL-14893; the 
description follows {{muteException}} since CAMEL-24478). Client streaming and 
bidirectional streaming calls go through the consumer strategy observers 
instead, and these never look at the exchange result:

* {{GrpcRequestAggregationStreamObserver.onCompleted}} (strategy 
{{AGGREGATION}}) sends {{exchange.getMessage().getBody()}} with {{onNext}} and 
then {{onCompleted}};
* {{GrpcRequestPropagationStreamObserver.onNext}} (strategy {{PROPAGATION}}, 
the default) sends the body of each exchange with {{onNext}}, and 
{{onCompleted}} completes the call.

So a route that fails after it built the response (a {{to(...)}} after the 
transformation, a validation, a database write) answers the client with a 
successful response, and the error stays on the server (it only goes to the 
consumer's exception handler). When the route fails before it built a response, 
the body is the request (or the list of requests), which the response 
marshaller cannot write; the call then fails with an unrelated error.

h3. Reproduction

{code:java}
from("grpc://localhost:0/org.apache.camel.component.grpc.PingPong?synchronous=true&consumerStrategy=AGGREGATION&muteException=false")
    .bean(new GrpcMessageBuilder(), "buildAggregatedPongResponse")   // 
List<PingRequest> -> PongResponse
    .throwException(CamelException.class, "GRPC Camel streaming exception 
message");
{code}

and the same with {{consumerStrategy=PROPAGATION}} and a bean that maps one 
{{PingRequest}} to a {{PongResponse}}. A client calling the client streaming 
method {{pingAsyncSync}} gets the {{PongResponse}} ({{pong_name: "PINGPONG"}}) 
and a normal completion; a unary call to the same failing route gets 
{{INTERNAL}}. A test with both strategies fails on main.

h3. Proposed fix

Both observers end the call with the same {{Status.INTERNAL}} error as the 
unary path when the exchange failed (shared helper 
{{GrpcMethodHandler.toStatusException}}, so {{muteException}} applies the same 
way). The propagation observer remembers the failure: the call is over, so 
later requests of the client are no longer routed and the response observer is 
not called again ({{onCompleted}} and {{onError}} of the client are still 
forwarded to the route when {{forwardOnCompleted}}/{{forwardOnError}} are set). 
A route that must keep a bidirectional stream open handles the exception 
({{onException(...).handled(true)}}). The {{DELEGATION}} strategy is unchanged: 
the route controls the response observer there. With the fix the new test and 
the whole camel-grpc suite pass. Clients now get errors where they got 
responses, so the change gets an upgrade guide note.

Affected: 4.14.x, 4.18.x and main (same observers; 4.14.x has no 
{{muteException}}, so a backport there uses the exception message as the unary 
path does).

Duplicate check (2026-10-01): JIRA component camel-grpc (all 41 issues) and 
text "grpc" with "aggregation"/"propagation"/"streaming" and "exception": only 
CAMEL-14893 (unary calls) and CAMEL-24478. GitHub pull requests "grpc 
exception", "grpc streaming": none about the streaming strategies.

_Filed with Claude Code on behalf of allthingssecurity._




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to