7
votes

I am using Retrofit 2 in my Android project. When I hit an API endpoint using a GET method and it returns a 400 level error I can see the error content when I use an HttpLoggingInterceptor, but when I get to the Retrofit OnResponse callback the error body's string is empty.

I can see that there is a body to the error, but I can't seem to pull that body when in the context of the Retrofit callback. Is there a way to ensure the body is accessible there?

Thanks, Adam

Edit: The response I see from the server is: {"error":{"errorMessage":"For input string: \"000001280_713870281\"","httpStatus":400}}

I am trying to pull that response from the response via: BaseResponse baseResponse = GsonHelper.getObject(BaseResponse.class, response.errorBody().string()); if (baseResponse != null && !TextUtils.isEmpty(baseResponse.getErrorMessage())) error = baseResponse.getErrorMessage();

(GsonHelper is just a helper which passes the JSON string through GSON to pull the object of type BaseResponse)

The call to response.errorBody().string() results in an IOException: Content-Length and stream length disagree, but I see the content literally 2 lines above in Log Cat

3
can you post your example code trying to access the body - snkashis
Have you found a solution to this problem? I'm struggling with the same issue, I can see en error response body in logs but errorBody content is empty. - Greg
I have not. I'm just handling any error from my API in a generic sense and not showing the meaningful error to the user. It's not ideal, but the only option I see before I understand why Retrofit / OkHttp are stripping the error info. - adamacdo
adamacdo, did you resolve your problem? I have the same issue.. Thank you very much - anthony
I have not. It was for a project I have since moved on from. I showed a generic error without the actual error from the server as a workaround. Not helpful, but literally better than nothing. - adamacdo

3 Answers

14
votes

I encountered the same problem before and I fixed it by using the code response.errorBody().string() only once. You'll receive the IOException if you are using it many times so it is advised to just use it as a one-shot stream just as the Documentation on ResponseBody says.

My suggestion is: convert the Stringified errorBody() into an Object immediately as the latter is what you're gonna be using on subsequent operations.

-1
votes

First create an Error class like below:

public class ApiError {
    @SerializedName("httpStatus")
    private int statusCode;
    @SerializedName("errorMessage")
    private String message;

    public ApiError() {

    }

    public ApiError(String message) {
        this.message = message;
    }

    public ApiError(int statusCode, String message) {
        this.statusCode = statusCode;
        this.message = message;
    }

    public int status() {
        return statusCode;
    }

    public String message() {
        return message;
    }

    public void setStatusCode(int statusCode) {
        this.statusCode = statusCode;
    }
}

Second you can create a Utils class to handle your error like below:

public final class ErrorUtils {
    private ErrorUtils() {

    }

    public static ApiError parseApiError(Response<?> response) {
        final Converter<ResponseBody, ApiError> converter =
                YourApiProvider.getInstance().getRetrofit()
                        .responseBodyConverter(ApiError.class, new Annotation[0]);

        ApiError error;
        try {
            error = converter.convert(response.errorBody());
        } catch (IOException e) {
            error = new ApiError(0, "Unknown error"
        }
        return error;
    }

And finally handle your error like below:

if (response.isSuccessful()) {
   // Your response is successfull
   callback.onSuccess();
   } 
else {
   callback.onFail(ErrorUtils.parseApiError(response));
   }

I hope this'll help you. Good luck.

-2
votes

If you are gettig 400 then its a bad request you r trying to send to server. check your get req.