Feign: Better exception handling

Created on 23 Sep 2015  路  19Comments  路  Source: OpenFeign/feign

Hello

  1. Is there a way to wrap the RetryableException exception? Something like exception interceptor?
  2. Would it make more sense to have 2 exceptions? One if something goes wrong while creating/executing request and one for server response? Right now based on the examples, I must parse the exception message to identify what went wrong. I would love to get a ServerException where I could read the status code.
documentation help wanted

Most helpful comment

@adriancole, if your message was for me, here are some stacktraces as example.

  • wrong domain
feign.RetryableException: my.domain.com executing POST https://my.domain.com/oauth/token?grant_type=password...
    at feign.FeignException.errorExecuting(FeignException.java:67)
    at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:104)
    at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76)
    at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103)
    at com.sun.proxy.$Proxy86.authenticate(Unknown Source)
    at com.mfg.client.AbstractMfgClientTest.initUserContext(AbstractMfgClientTest.java:35)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: java.net.UnknownHostException: my.domain.com
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:184)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:589)
    at sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:668)
    at sun.net.NetworkClient.doConnect(NetworkClient.java:175)
    at sun.net.www.http.HttpClient.openServer(HttpClient.java:432)
    at sun.net.www.http.HttpClient.openServer(HttpClient.java:527)
    at sun.net.www.protocol.https.HttpsClient.<init>(HttpsClient.java:264)
    at sun.net.www.protocol.https.HttpsClient.New(HttpsClient.java:367)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(AbstractDelegateHttpsURLConnection.java:191)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1105)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:999)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:177)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1513)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441)
    at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:338)
    at feign.Client$Default.convertResponse(Client.java:152)
    at feign.Client$Default.execute(Client.java:74)
    at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:97)
    ... 33 more
  • remote SSL issue (bad certificate)
feign.RetryableException: Remote host closed connection during handshake executing POST https://my.domain.com/oauth/token?grant_type=password&...
    at feign.FeignException.errorExecuting(FeignException.java:67)
    at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:104)
    at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76)
    at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103)
    at com.sun.proxy.$Proxy86.authenticate(Unknown Source)
    at com.mfg.client.AbstractMfgClientTest.initUserContext(AbstractMfgClientTest.java:35)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:992)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1513)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441)
    at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:338)
    at feign.Client$Default.convertResponse(Client.java:152)
    at feign.Client$Default.execute(Client.java:74)
    at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:97)
    ... 33 more
Caused by: java.io.EOFException: SSL peer shut down incorrectly
    at sun.security.ssl.InputRecord.read(InputRecord.java:505)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:973)
    ... 45 more
  • connection refused
java.net.ConnectException: Connection refused
    java.net.PlainSocketImpl.socketConnect(Native Method)
    java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    java.net.Socket.connect(Socket.java:589)
    sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:668)
    sun.net.NetworkClient.doConnect(NetworkClient.java:175)
    sun.net.www.http.HttpClient.openServer(HttpClient.java:432)
    sun.net.www.http.HttpClient.openServer(HttpClient.java:527)
    sun.net.www.protocol.https.HttpsClient.<init>(HttpsClient.java:264)
    sun.net.www.protocol.https.HttpsClient.New(HttpsClient.java:367)
    sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(AbstractDelegateHttpsURLConnection.java:191)
    sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1105)
    sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:999)
    sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:177)
    sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1513)
    sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441)
    java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
    sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:338)
    feign.Client$Default.convertResponse(Client.java:152)
    feign.Client$Default.execute(Client.java:74)
    feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:97)
    feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76)
    feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103)
    com.sun.proxy.$Proxy81.authenticate(Unknown Source)
    com.mfg.web.security.MfgAuthenticationProvider.authenticate(MfgAuthenticationProvider.java:31)
    org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174)

All 19 comments

Yeah this is about time. A nullable error code/response body in the server
exception would be a great start. Want to start by adding that?

So my idea is to have multiple exceptions

  1. EmptyResponseException (if the response is empty but shouldn't be)

    • status code

  2. ErrorResponseException (when server returns 4xx - 5xx)

    • status code

    • message

  3. RequestException (when generating request but something goes wrong)
  4. RetryableException (when executing request, but can't reach the server - network problem?)

Do you think this covers all the situations? Do you recommend better names? Also should RetryableException cover more situations? For example DNS name not resolving, timeout, connection broke down, ...?

Interesting ideas. I think the first step is to leave the hierarchy alone
and just add a nullable error code to the existing exception. That allows
you to match vs make a _lot_ of exception types.

Could we do that step first? It would be easy to map on error code, at
least in the projects I've worked on.

If we do new add types, I'd prefer to add ones where the behavior is very
explicit. Otherwise use some sort of default exception, or map the cause to
something default.

An example is I think our encoding exceptions are near useless. They just
Clutter the interface and I suspect if we do a github search no one is
catching them.

I feel like this for the empty response exception. I don't think there's a
generic use case for empty response. I mean that is usually a bug, so you
could use something like illegal state or something.

Hi @adriancole

For now I have added the status code. I like how exceptions are structured in this project https://github.com/reon-io/reon-sdk/tree/101b077f2df5635eb88421f062f4c45461582811/reon-api-java/src/main/java/io/reon/http . Should we do something similar?

By start with response code, I meant let's just add the response code. An
exception hierarchy and corresponding tests would become a large percentage
of the codebase, lost on many.

Adding response code is a great improvement as you can switch on it, or do
a range comparison in a catch.

Is it possible to provider such a feature: automatically decode exception objects depend on the declared thrown types.

For example:

interface GitHub {
  @RequestLine("GET /contributor/{id}")
  Contributor contributor(@Param("id") String id) thrown NoSuchContributorFoundException;
}

If the response status is 404, throw NoSuchContributorFoundException instead of FeignException.

I tried to write my own ErrorDecoder implementation. But it is hard to provide this feature. Currently the interface ErrorDecoder provides decode(String methodKey, Response response) method to overwrite. But if it also provides a decode(String methodKey, Response response, MethodMetadata metadata) method, and my decoder can get the thrown exception classes from the MethodMetadata object. It will be easy to realize the requirement.

I think trying to magically lookup the exceptions you've declared will
require you to coordinate a Contract with your ErrorDecoder.

Ex. you can make a version of Contract that shares state with your
ErrorDecoder. For example, this can seed a Map
metadataByConfigKey for your ErrorDecoder to read later. Then when you
decode, you can lookup methodKey in that map.

Regardless of how you do this (map or otherwise) Contract is where you
will end up integrating this.

@adriancole Ok, I will have a try. Thanks for your help.

Hi all,
What about technical exception like : java.net.UnknownHostException or java.net.ConnectException (Connection refused) or java.io.EOFException (SSL peer shut down incorrectly). I'd like to wrap them with a generic ClientUnavailableException and have a centralized way of handling them (user does not need all the details for the unavailability).
The error decoder is working well for error that happens after a request is complete, but i don't know / see where to plug in a wrapper. Any idea ?

For example, with an unknown host, here is a stacktrace I've got :

Caused by: java.net.UnknownHostException: non.existing-domain.com
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:184)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:589)
    at sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:668)
    at sun.net.NetworkClient.doConnect(NetworkClient.java:175)
    at sun.net.www.http.HttpClient.openServer(HttpClient.java:432)
    at sun.net.www.http.HttpClient.openServer(HttpClient.java:527)
    at sun.net.www.protocol.https.HttpsClient.<init>(HttpsClient.java:264)
    at sun.net.www.protocol.https.HttpsClient.New(HttpsClient.java:367)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(AbstractDelegateHttpsURLConnection.java:191)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1105)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:999)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:177)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1513)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441)
    at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:338)
    at feign.Client$Default.convertResponse(Client.java:152)
    at feign.Client$Default.execute(Client.java:74)
    at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:97)

The SynchronousMethodHandler catches the exception (exception is wrapped inside RetryableException) and retry the action. But the known is still unknown, so that it retries ... again and again (until maxAttempts is reached). We may need a way to interrupt this as soon as encounter. Also, it would be nice to wrap all connection error into an umbrella ConnectionException to ease handling on te client side.

can you paste a stack trace?

@adriancole, if your message was for me, here are some stacktraces as example.

  • wrong domain
feign.RetryableException: my.domain.com executing POST https://my.domain.com/oauth/token?grant_type=password...
    at feign.FeignException.errorExecuting(FeignException.java:67)
    at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:104)
    at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76)
    at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103)
    at com.sun.proxy.$Proxy86.authenticate(Unknown Source)
    at com.mfg.client.AbstractMfgClientTest.initUserContext(AbstractMfgClientTest.java:35)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: java.net.UnknownHostException: my.domain.com
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:184)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:589)
    at sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:668)
    at sun.net.NetworkClient.doConnect(NetworkClient.java:175)
    at sun.net.www.http.HttpClient.openServer(HttpClient.java:432)
    at sun.net.www.http.HttpClient.openServer(HttpClient.java:527)
    at sun.net.www.protocol.https.HttpsClient.<init>(HttpsClient.java:264)
    at sun.net.www.protocol.https.HttpsClient.New(HttpsClient.java:367)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(AbstractDelegateHttpsURLConnection.java:191)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1105)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:999)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:177)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1513)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441)
    at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:338)
    at feign.Client$Default.convertResponse(Client.java:152)
    at feign.Client$Default.execute(Client.java:74)
    at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:97)
    ... 33 more
  • remote SSL issue (bad certificate)
feign.RetryableException: Remote host closed connection during handshake executing POST https://my.domain.com/oauth/token?grant_type=password&...
    at feign.FeignException.errorExecuting(FeignException.java:67)
    at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:104)
    at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76)
    at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103)
    at com.sun.proxy.$Proxy86.authenticate(Unknown Source)
    at com.mfg.client.AbstractMfgClientTest.initUserContext(AbstractMfgClientTest.java:35)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:992)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1513)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441)
    at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:338)
    at feign.Client$Default.convertResponse(Client.java:152)
    at feign.Client$Default.execute(Client.java:74)
    at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:97)
    ... 33 more
Caused by: java.io.EOFException: SSL peer shut down incorrectly
    at sun.security.ssl.InputRecord.read(InputRecord.java:505)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:973)
    ... 45 more
  • connection refused
java.net.ConnectException: Connection refused
    java.net.PlainSocketImpl.socketConnect(Native Method)
    java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    java.net.Socket.connect(Socket.java:589)
    sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:668)
    sun.net.NetworkClient.doConnect(NetworkClient.java:175)
    sun.net.www.http.HttpClient.openServer(HttpClient.java:432)
    sun.net.www.http.HttpClient.openServer(HttpClient.java:527)
    sun.net.www.protocol.https.HttpsClient.<init>(HttpsClient.java:264)
    sun.net.www.protocol.https.HttpsClient.New(HttpsClient.java:367)
    sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(AbstractDelegateHttpsURLConnection.java:191)
    sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1105)
    sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:999)
    sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:177)
    sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1513)
    sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441)
    java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
    sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:338)
    feign.Client$Default.convertResponse(Client.java:152)
    feign.Client$Default.execute(Client.java:74)
    feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:97)
    feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76)
    feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103)
    com.sun.proxy.$Proxy81.authenticate(Unknown Source)
    com.mfg.web.security.MfgAuthenticationProvider.authenticate(MfgAuthenticationProvider.java:31)
    org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174)

Hi!
I've arrived to this thread becuase we are using Feign with Consul using Spring-boot, and we'd love to manage somehow the different errors that our clients could rise. So because of that, do you have any idea about if we'd be able to deal with all of this type of exceptions? Do you have anything we can start to look for (ie. _beta/feature/experimental_ branch)?
Best,

@astik etc.. looking at the above exception, feign is coercing this to a RetryableException.

I suspect that this means the Retryer will kick in, which allows you to decide to retry or propagate the cause. Instead of throwing the RetryableException, you could throw a different type instead, via a custom Retryer.

Wanna give that a shot?

Seems to be a good idea indeed, it should work for most of the exception. I'll give it a try and we'll come back to you as soon as i have news =)

@adriancole you were right ! Defining a custom Retryer is a good solution.

Here is the code for mine :

package com.my.client.feign;

import java.io.IOException;

import com.my.client.error.MyClientTechnicalException;

import feign.RetryableException;
import feign.Retryer;

public class MyRetryer extends Retryer.Default {
  @Override
  public void continueOrPropagate(RetryableException exception) {
    if (exception.getCause() != null && IOException.class.isAssignableFrom(exception.getCause().getClass())) {
      throw new MyClientTechnicalException();
    }
    super.continueOrPropagate(exception);
  }

  @Override
  public Retryer clone() {
    return new MyRetryer();
  }
}

I think we can solve this issue by documenting these specific work arounds.

I've came across this thread as I was looking for a way to wrap DecodeException or FeignException further with my custom exceptions. This is a Spring Boot project. Is there a better way besides wrapping FeignClient's?

@adriancole i have tried this workaround to handle Retryable Exception. But i am facing Two Issues.

  1. if i get a HysterixTimeoutException (can not handle in decoder or Retryer)
    2.How can i get Information about which Service Resulted In error in Retryer. i am able to get path for which request Failed but In my case it is Quite difficult to get ServiceId or Name from path. is there any other way.

We've made a number of enhancements to FeignException. It now contains the original Request object and the Response body. We've also expanded it to allow for finer grained, status driven exceptions. Based on these changes I'm going to close this issue and ask that if anyone interested has any remaining questions, please open a new issue so we can work your specific situation.

Was this page helpful?
0 / 5 - 0 ratings