shashank created CAMEL-25247:
--------------------------------

             Summary: camel-thrift - the consumer does not send a route failure 
to the client: the synchronous server ignores it, the asynchronous server 
writes both the error and a response, so the error answers the next call on the 
connection
                 Key: CAMEL-25247
                 URL: https://issues.apache.org/jira/browse/CAMEL-25247
             Project: Camel
          Issue Type: Bug
            Reporter: shashank


{{ThriftMethodHandler}} turns a Thrift call into an exchange. It does not 
handle a failed exchange correctly in either server mode:

# *Asynchronous server (the default, {{THsHaServer}}).* The completion callback 
does
{code:java}
if (exception != null) {
    callback.onError(exception);
}
message = exchange.getMessage();
...
callback.onComplete(response);
{code}
so for a failed exchange it calls {{onError}} *and then* {{onComplete}}. For a 
{{void}} method, and for any method whose generated {{onComplete}} can write a 
result (an object return type such as a struct or a string, or a body of the 
failed exchange that converts to the return type), two results are written for 
one call. With a framed client on one connection, a failing {{void ping()}} 
returns normally, and the {{TApplicationException}} meant for it is read as the 
answer to the *next* call ({{add(12, 13)}} fails with "Forced"); every later 
answer on that connection is shifted by one. (For a method returning a 
primitive whose failed body converts to {{null}}, the generated {{onComplete}} 
fails on unboxing before it writes, so only the error is sent: that is why the 
asynchronous {{calculate}} case below passes on main.)
# *Synchronous server ({{synchronous=true}}).* After 
{{consumer.getProcessor().process(exchange)}} the exception of the exchange is 
never looked at: a {{void}} method returns normally (the client sees success), 
any other method returns the body of the failed exchange converted to its 
return type, or fails with {{TApplicationException: Return type requires not 
empty body}} when it does not convert, and an exception declared by the IDL 
({{throws (1:InvalidOperation ouch)}}) thrown by the route never reaches the 
client as such.

The code has been like this since the component was added (CAMEL-11333, 2017).

h3. Reproduction

Routes for the test {{Calculator}} service ({{add}} answers 25, {{calculate}} 
throws {{new InvalidOperation(1, "Forced")}}, other methods throw 
{{IllegalStateException("Forced")}}), one with {{synchronous=true}} and one 
with the default asynchronous server, and a {{Calculator.Client}} over 
{{TFramedTransport}}:

* synchronous: {{calculate}} fails with {{TApplicationException}} instead of 
{{InvalidOperation}}; {{ping}} returns normally;
* asynchronous: {{ping}} returns normally and the following {{add(12, 13)}} on 
the same connection throws {{TApplicationException: Forced}}.

The test fails on main for these three cases (the asynchronous {{calculate}} 
case passes on main, see above).

h3. Proposed fix

* synchronous handler: throw the exception of the exchange, so the Thrift 
processor writes a declared exception as such and any other one as 
{{TApplicationException}};
* asynchronous handler: complete the call exactly once, with 
{{onError(exception)}} when the exchange failed, otherwise with 
{{onComplete(response)}}; the "unable to detect the return type" and "null 
message" errors also return instead of falling through to {{onComplete}}.

With the fix the new test ({{ThriftConsumerExceptionTest}}, 4 tests) and the 
whole camel-thrift suite (45 tests) pass. Clients now get errors for failed 
exchanges, so the change gets an upgrade guide note.

Affected: 4.14.x, 4.18.x and main (same code).

Duplicate check (2026-10-01): JIRA text "thrift" (26 issues; no component 
filter matches) : nothing about exceptions or failed exchanges in the consumer 
(CAMEL-24442 is the data format, CAMEL-16133 multiplexing). GitHub pull 
requests "thrift": none about it.

_Filed with Claude Code on behalf of allthingssecurity._




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

Reply via email to