Retrofit: Receiving NULL on response error body if the status code is 400

Created on 27 Oct 2015  Â·  27Comments  Â·  Source: square/retrofit

Hi I get response as null in Retrofit error body response if the response code is 400. I even inspected the issue by applying HttpLoggingInceptor and as seen in the below log you get the response in Okhttp but it really never comes to Retrofit hence stucked and confuse at this situation. For code 200 it works perfectly fine

<-- HTTP/1.1 400 Bad Request (2080ms)
Access-Control-Allow-Origin: http://ec2-52-89-241-222.us-west-2.compute.amazonaws.com:3000
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: origin, content-type, accept
Server: akka-http/2.3.14
Date: Tue, 27 Oct 2015 07:40:25 GMT
Content-Type: application/json
Content-Length: 54
OkHttp-Selected-Protocol: http/1.1
OkHttp-Sent-Millis: 1445931611243
OkHttp-Received-Millis: 1445931612103
{
"appStatusCode" : 100,
"data" : "UNKNOWN_TEAM"
}
<-- END HTTP (54-byte body)
code = 400
ERROR: null

Needs Info

Most helpful comment

check your JSON response means how u r getting JSON for the success and failure cases

public void onResponse(Call call, Response response) {

if (response.isSuccessful()) {

    if (response.body().getStatus() == 201) {
        Toast.makeText(cxt, "WALLET", Toast.LENGTH_LONG).show();
                //ToDo we can handle here
       //DO AS PER YOUR REQUIREMENT
    }
} else {
    if (response.code() == 400) {
        Gson gson = new GsonBuilder().create();
       MovieErrorResponse mError = new MovieErrorResponse();
        try {
            mError = gson.fromJson(response.errorBody().string(), MovieErrorResponse.class);
            if(mError.getMessage().equals("Wallet  not enough."))
            {
                if(mError.getWalletBalance()==null)
                {
                    mError.setWalletBalance("0");
                }

                //ToDo we can handle here

            }
        } catch (IOException e) {
            // handle failure to read error
        }
    }
    else {
      Gson gson=new GsonBuilder().create();
        MvPurchaseTokenErrorResponse mverror=new MvPurchaseTokenErrorResponse();
        try {
            mverror=gson.fromJson(response.errorBody().string(),MvPurchaseTokenErrorResponse.class);
            if(mverror.isStatus()==false)
            {
                //ToDo we can handle here
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        //ToDo we can handle here(  OR ELSE )

    }
}

}

All 27 comments

It sent you Response data as I see in the log. But You probably did not handle the error properly. In the onResponse method, you can check if result is successful by calling 'response.isSuccess()' , if it's not successful then, you'll need to handle the Response body yourself. If you use GsonConverter then you'll need a POJO class for the following json object to Handle the response properly.

{
"appStatusCode" : 100,
"data" : "UNKNOWN_TEAM"
}

I have already written that it works perfectly for status code 200 which implies that I have already created POJO class and was able to successfully parse the json. But this fails for status code 400 and response.onErrorBody() gives me null whereas I can see the response in Okhttp hence I have asked for help

As far I know, That gives you null because may be onResponse callback have been called and you'll get that JSON when response.isSuccess() is false. Try to write log in both callback methods and check which callback is called when you have the described situation.

No I am afraid you are wrong. I appreciate you helping around with the answer but that is not the case would wait for author and other admin to reply on this issue

Yeah, might be wrong, it depends :) But I did not gave you any answer, just suggested a few things to find the problem with some explanations from my experience. Hopefully other experienced contributors will sort it out for you.

How are you logging the error?

Using this in code and with its respective gradle dependencies

OkHttpClient client = new OkHttpClient();
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
client.interceptors().add(interceptor);

Sorry I meant what methods are you calling in the consuming code that gives you the null?

I am using the same code as there in example describe you. heres what I am doing in onResponse

public void onResponse(Response response, Retrofit retrofit) {
progressDialog.dismiss();
Log.d("response", "code = " + response.code());
if(response.isSuccess()){
Log.d("response", "code = " + response.code());
Log.d("response", "code = " + response.body());
ServerStatus serverStatus = response.body();
if(serverStatus.getAppStatusCode().equalsIgnoreCase(WorkoutCashConstants.SUCCESS_API)){
Log.d("response", "success " );
Intent intent = new Intent(OnboardTeamNameActivity.this, OnboardEmailActivity.class);
intent.putExtra("TeamName",etTeamName.getText().toString().trim());
startActivity(intent);
}

                                }else {

                                    Log.d("response", "code = " + response.code());
                                    Log.d("response: ","boyd= "+response.body());

                                    Converter<ResponseBody, Error> errorConverter =
                                            retrofit.responseConverter(Error.class, new Annotation[0]);
                                    // Convert the error body into our Error type.
                                    try {
                                        Error error = errorConverter.convert(response.errorBody());
                                        System.out.println("ERROR: " + error.message);
                                    }catch (IOException e){

                                    }
                                }

                            }

There is no "message" key in the response data that you posted so it will always be null.

You mean there should be a "message" key in my response Json ??? Then only I can get the response ??

Your response is

{
"appStatusCode" : 100,
"data" : "UNKNOWN_TEAM"
}

but when you decode it, you try to log a "message" field which does not exist:

Error error = errorConverter.convert(response.errorBody());
System.out.println("ERROR: " + error.message);

so the expect output here is null. Try logging error.data or error.appStatusCode and you'll see the data.

Damn Stupid of me it works. Thnx a ton Jake I owe you a beer !!! :D

@JakeWharton I got a follow up question, If I want to deserialize error body in RxJava way, how can I do it?

Because I can't get retrofit instance just like onResponse(.., Retrofit retrofit) callback provided in RxJava way.

Retrofit was removed from onResponse, so the Retrofit instance needs to be
propagated manually in both cases.

On Thu, Dec 17, 2015, 6:37 AM Saiday [email protected] wrote:

@JakeWharton https://github.com/JakeWharton I got a follow up question,
If I want to deserialize error body in RxJava way, how can I do it?

Because I can't get Retrofit retrofit instance just like onResponse(..)
callback provided in RxJava way.

—
Reply to this email directly or view it on GitHub
https://github.com/square/retrofit/issues/1235#issuecomment-165431131.

@JakeWharton is this the right way to print network error message?

public void onError(Throwable e) {
                            if (e instanceof HttpException) {
                                HttpException exception = (HttpException) e;
                                Response response = exception.response();
                                try {
                                    JSONObject jObjError = new JSONObject (response.errorBody().string());
                                    Log.e("Error ","" + jObjError.optString("message"));
                                } catch (JSONException e1) {
                                    e1.printStackTrace();
                                } catch (IOException e1) {
                                    e1.printStackTrace();
                                }
                            }

                        }

@JakeWharton I understand that in case of the request being successfully processed by the server the onResponse will be called and in case it is not having the success status the errorBody will get populated. Now in this case I have to convert the errorBody into the POJO in the UI thread. Is there any way I can delegate that task also to the converterFactory to get the response getting converted before on response is been called?

Not using the built-in Call type, no. This has been brought up before, but the problem is that you cannot trust the response body format of non-200 messages (actually you can't even trust 200s) because they could be coming from an intermediate source such as a load balancer, frontend HTTP server, proxy, captive web portal, etc. which can serve any format that it wants.

That said, if you really want this you should be able to create your own type which accepts two generic parameters and attempts deserialization of errors. You would write a CallAdapter.Factory and CallAdapter for this type and then delegate to Retrofit's callbackExecutor() before calling your version of Callback.

Thanks! @JakeWharton.

It would be really nice if you could please direct me to some post where I can see how do I delegate these custom classes to said executor.

You can look at the built-in CallAdapter which is used for Call:
https://github.com/square/retrofit/blob/34eb99f46c9f8312563bbcf07345b4ed5354f6ca/retrofit/src/main/java/retrofit2/ExecutorCallAdapterFactory.java

On Sun, Aug 21, 2016 at 6:27 PM Anant Anand Gputa [email protected]
wrote:

Thanks! @JakeWharton https://github.com/JakeWharton.

It would be really nice if you could please direct me to some post where I
can see how do I delegate these custom classes to said executor.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/square/retrofit/issues/1235#issuecomment-241286284,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAEEEc6ciO6Hsg6wHAljciUnW6dMH6e5ks5qiNDPgaJpZM4GWQtg
.

Thanks!

hI,if my response.isSuccessful is true then I am able to fetch the response.body() properly.in else part i am unable to fetch the resonse.body() ..from the code it is returning null
if i try with postman I am getting response as body what exactly I am getting from server.
May I know why I am getting response.body() as null through code??
IN POSTMAN
If it is sucess i am getting response.body() as
{
"status": 201,
"message": "Success",
"walletBalance": "8266.16",
"currency": "GBP"
}
In another case i am getting response.body()
{
"status": 400,
"message": "Wallet balance is not enough. Please recharge your wallet balance",
"walletBalance": null,
"currency": "GBP"
}
I am unable to fetch the above data

@MNGsulochana Hi, did you solve the project with response 201 yet?

resolved

How do you handle it?

On Mon, May 7, 2018, 12:28 AM MNGsulochana notifications@github.com wrote:

resolved

—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/square/retrofit/issues/1235#issuecomment-386896056,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AK35EQyJj32Ei7aqxUs5t7mlPzcdosMaks5tvzLLgaJpZM4GWQtg
.

check your JSON response means how u r getting JSON for the success and failure cases

public void onResponse(Call call, Response response) {

if (response.isSuccessful()) {

    if (response.body().getStatus() == 201) {
        Toast.makeText(cxt, "WALLET", Toast.LENGTH_LONG).show();
                //ToDo we can handle here
       //DO AS PER YOUR REQUIREMENT
    }
} else {
    if (response.code() == 400) {
        Gson gson = new GsonBuilder().create();
       MovieErrorResponse mError = new MovieErrorResponse();
        try {
            mError = gson.fromJson(response.errorBody().string(), MovieErrorResponse.class);
            if(mError.getMessage().equals("Wallet  not enough."))
            {
                if(mError.getWalletBalance()==null)
                {
                    mError.setWalletBalance("0");
                }

                //ToDo we can handle here

            }
        } catch (IOException e) {
            // handle failure to read error
        }
    }
    else {
      Gson gson=new GsonBuilder().create();
        MvPurchaseTokenErrorResponse mverror=new MvPurchaseTokenErrorResponse();
        try {
            mverror=gson.fromJson(response.errorBody().string(),MvPurchaseTokenErrorResponse.class);
            if(mverror.isStatus()==false)
            {
                //ToDo we can handle here
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        //ToDo we can handle here(  OR ELSE )

    }
}

}

from @MNGsulochana answers,

onResponse() {
        if (response.isSuccessful()) {
            res = response.body();
        } else {
            Gson gson = new Gson();
            errorRes = gson.fromJson(response.errorBody().string(), xxx.class);
        }
    }

May be it will helps for new ones

Was this page helpful?
0 / 5 - 0 ratings