Authentication error handling with Java client on HttpClient5 transport

Versions (relevant - OpenSearch/Dashboard/Server OS/Browser):

OpenSearch 2.11.1
OpenSearch Java client 2.9.0
Java 17

Describe the issue:

When issuing a request with invalid authentication credentials and server responds with 401 (Unauthorized), the client still tries to parse the response. This of course fails because the body is empty or does not correspond to the expected successful response. This results in a NoSuchElementException in JacksonJsonParser instead of proper exception from which the caller can identify that the cause was authentication issue.

Digging through the client code it appears that this is because of special handling of 400,401,403,404,405 error code (see opensearch-java/java-client/src/main/java/org/opensearch/client/transport/httpclient5/ApacheHttpClient5Transport.java at main · opensearch-project/opensearch-java · GitHub).

Is there a proper way to distinguish the errors caused by bad authentication credentials (401 response code) from general malfunctions in response parsing?

Configuration:

Relevant Logs or Screenshots:

java.util.NoSuchElementException
at org.opensearch.client.json.jackson.JacksonJsonpParser.next(JacksonJsonpParser.java:139)
at org.opensearch.client.json.JsonpDeserializer.deserialize(JsonpDeserializer.java:81)
at org.opensearch.client.json.ObjectBuilderDeserializer.deserialize(ObjectBuilderDeserializer.java:91)
at org.opensearch.client.json.DelegatingDeserializer$SameType.deserialize(DelegatingDeserializer.java:55)
at org.opensearch.client.transport.httpclient5.ApacheHttpClient5Transport.prepareResponse(ApacheHttpClient5Transport.java:501)
at org.opensearch.client.transport.httpclient5.ApacheHttpClient5Transport.lambda$performRequestAsync$0(ApacheHttpClient5Transport.java:178)
at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:646)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2147)
at org.opensearch.client.transport.httpclient5.ApacheHttpClient5Transport$1.completed(ApacheHttpClient5Transport.java:223)
at org.opensearch.client.transport.httpclient5.ApacheHttpClient5Transport$1.completed(ApacheHttpClient5Transport.java:212)
at org.apache.hc.core5.concurrent.BasicFuture.completed(BasicFuture.java:123)
at org.apache.hc.core5.concurrent.ComplexFuture.completed(ComplexFuture.java:72)
at org.apache.hc.client5.http.impl.async.InternalAbstractHttpAsyncClient$1$1.completed(InternalAbstractHttpAsyncClient.java:280)
at org.apache.hc.core5.http.nio.support.AbstractAsyncResponseConsumer$1.completed(AbstractAsyncResponseConsumer.java:101)
at org.apache.hc.core5.http.nio.entity.AbstractBinAsyncEntityConsumer.completed(AbstractBinAsyncEntityConsumer.java:84)
at org.apache.hc.core5.http.nio.entity.AbstractBinDataConsumer.streamEnd(AbstractBinDataConsumer.java:81)
at org.apache.hc.core5.http.nio.support.AbstractAsyncResponseConsumer.streamEnd(AbstractAsyncResponseConsumer.java:142)
at org.apache.hc.client5.http.impl.async.HttpAsyncMainClientExec$1.streamEnd(HttpAsyncMainClientExec.java:251)
at org.apache.hc.core5.http.impl.nio.ClientHttp1StreamHandler.dataEnd(ClientHttp1StreamHandler.java:270)
at org.apache.hc.core5.http.impl.nio.ClientHttp1StreamDuplexer.dataEnd(ClientHttp1StreamDuplexer.java:366)
at org.apache.hc.core5.http.impl.nio.AbstractHttp1StreamDuplexer.onInput(AbstractHttp1StreamDuplexer.java:335)
at org.apache.hc.core5.http.impl.nio.AbstractHttp1IOEventHandler.inputReady(AbstractHttp1IOEventHandler.java:64)
at org.apache.hc.core5.http.impl.nio.ClientHttp1IOEventHandler.inputReady(ClientHttp1IOEventHandler.java:41)
at org.apache.hc.core5.reactor.ssl.SSLIOSession.decryptData(SSLIOSession.java:609)
at org.apache.hc.core5.reactor.ssl.SSLIOSession.access$200(SSLIOSession.java:74)
at org.apache.hc.core5.reactor.ssl.SSLIOSession$1.inputReady(SSLIOSession.java:202)
at org.apache.hc.core5.reactor.InternalDataChannel.onIOEvent(InternalDataChannel.java:142)
at org.apache.hc.core5.reactor.InternalChannel.handleIOEvent(InternalChannel.java:51)
at org.apache.hc.core5.reactor.SingleCoreIOReactor.processEvents(SingleCoreIOReactor.java:178)
at org.apache.hc.core5.reactor.SingleCoreIOReactor.doExecute(SingleCoreIOReactor.java:127)
at org.apache.hc.core5.reactor.AbstractSingleCoreIOReactor.execute(AbstractSingleCoreIOReactor.java:86)
at org.apache.hc.core5.reactor.IOReactorWorker.run(IOReactorWorker.java:44)
at java.base/java.lang.Thread.run(Thread.java:840)

@jeecha could you please create a Github issue? thank you