19

I'm new to Retrofit. I have used Volley, and I kind of like Retrofit. I was just about to select Retrofit when I ran into this very non-descriptive error message when trying to do a POST.

 Exception in thread "main" retrofit.RetrofitError
    at retrofit.RetrofitError.httpError(RetrofitError.java:37)
    at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:413)
    at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:282)
    at myapi.api.$Proxy7.logon(Unknown Source)
    at myapi.api.TestDriver.main(TestDriver.java:94)

Well, I must say that this type of error message is about as useful as a warm jacket in the Sahara.

Does anyone even know where to begin with debugging this type of message? I really am not about to delegate to a REST api that does not provide useful error messages.

Joshua Pinter
  • 45,245
  • 23
  • 243
  • 245
user3186731
  • 441
  • 2
  • 5
  • 10

3 Answers3

21

You probably want to add a catch clause to TestDriver.main:

try {
  service.logon();
} catch (RetrofitError e) {
  System.out.println(e.getResponse().getStatus());
}
Jesse Wilson
  • 39,078
  • 8
  • 121
  • 128
  • Hey Jesse, can you you take a look at http://stackoverflow.com/questions/21398598/how-can-i-post-raw-whole-json-in-the-body-of-a-retrofit-request – user3186731 Jan 29 '14 at 02:13
  • 5
    RetrofitError doesn't always have a getResponse so you have to check for null. – Kalel Wade Sep 10 '14 at 20:21
21

Create a custom ErrorHandler for Retrofit.

I found that catching the error didn't provide a whole lot of extra information but creating a custom ErrorHandler for Retrofit allowed me to dig deeper into the actual error, like so:

class MyErrorHandler implements ErrorHandler {
  @Override public Throwable handleError(RetrofitError cause) {
    Response r = cause.getResponse();
    if (r != null && r.getStatus() == 401) {
      return new UnauthorizedException(cause);
    }
    return cause;
  }
}

RestAdapter restAdapter = new RestAdapter.Builder()
    .setEndpoint("https://api.github.com")
    .setErrorHandler(new MyErrorHandler())
    .setLogLevel(RestAdapter.LogLevel.FULL)  // Do this for development too.
    .build();

From the Custom Synchronous Error Handling section on Retrofit Page.

Set the Log Level to FULL as well, as shown in above config code.

droidev
  • 7,352
  • 11
  • 62
  • 94
Joshua Pinter
  • 45,245
  • 23
  • 243
  • 245
  • 2
    Note: If you are uploading large files with Retrofit, it's imperative that you turn off the `Log Level`, otherwise, it tries to process the entire file in the Log and will give you an elusive `OutOfMemoryError`. – Joshua Pinter Jul 04 '14 at 15:56
  • Using retrofit 1.6.1, 401 errors didn't give me a response. So instead, I had to check the message cause.getMessage().contains("authentication") to check if its a 401 or not. – Kalel Wade Sep 10 '14 at 20:20
4

Unfortunately RetrofitError (1.6.1) was tricky. 401's made getResponse() always return null which made it hard to tell if its a connection issue or authentication issue. At least for me, I had to look at the message to get the 401 error. Hopefully this helps someone else trying to do something similar.

public class RetrofitErrorHandler implements ErrorHandler {

    @Override
    public Throwable handleError(RetrofitError cause) {

        if (cause.isNetworkError()) {
            if(cause.getMessage().contains("authentication")){
                //401 errors
                return  new Exception("Invalid credentials. Please verify login info.");
            }else if (cause.getCause() instanceof SocketTimeoutException) {
                //Socket Timeout
                return new SocketTimeoutException("Connection Timeout. " +
                        "Please verify your internet connection.");
            } else {
                //No Connection
                return new ConnectException("No Connection. " +
                        "Please verify your internet connection.");
            }
        } else {

            return cause;
        }
    }

}
Kalel Wade
  • 7,742
  • 3
  • 39
  • 55