204

I'm using Retrofit 2.0.0-beta1.

In tests i have an alternate scenario and expect error HTTP 400

I would like to have retrofit.Response<MyError> response but response.body() == null

MyError is not deserialised - i see it only here

response.errorBody().string()

but it doesn't give me MyError as object

chubao
  • 5,871
  • 6
  • 39
  • 64
Piotr Boho
  • 2,650
  • 2
  • 13
  • 20

31 Answers31

215

I currently use a very easy implementation, which does not require to use converters or special classes. The code I use is the following:

public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
    DialogHelper.dismiss();

    if (response.isSuccessful()) {
        // Do your success stuff...
    } else {
        try {
            JSONObject jObjError = new JSONObject(response.errorBody().string());
            Toast.makeText(getContext(), jObjError.getJSONObject("error").getString("message"), Toast.LENGTH_LONG).show();
        } catch (Exception e) {
            Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_LONG).show();
        }
    }
}

A point to note here is that response.errorBody().string() will return the correct value only once. If you call it again, it will return an empty string. So in case you want to reuse it, store the value in a variable with the first call.

There is a way to get the error body string from the response without making it empty on the next call, by rolling your own implementation of toString() that does not update the errorBody Buffer's read-pointer. See this answer for more info.

ErikE
  • 48,881
  • 23
  • 151
  • 196
Saif Bechan
  • 16,551
  • 23
  • 83
  • 125
  • 7
    Your solution doesn't show an error response content. – CoolMind Sep 18 '16 at 17:21
  • 2
    Check my edit, dunno why I made it so unclear in the first place. – Saif Bechan Sep 19 '16 at 07:57
  • 7
    Finally, a simple answer to an android question that works (most android answers are comically complex). – Doug Voss Jul 07 '17 at 21:08
  • This is definitely not answering the question. It simply return the error message and not the Error Enum Object. Please follow this response : https://stackoverflow.com/a/21103420/2914140 – Tobliug Aug 18 '17 at 13:01
  • In the answer it's looking for a mapping to "message", but my error response didn't have that. It had a mapping to "error". So everyone reading, it depends on the response you get! – mco Aug 15 '18 at 20:57
  • If you need to use response.errorBody().string() more than once, check this answer out: https://stackoverflow.com/a/73857446/9952567 – Vladimir Kattsyn Sep 26 '22 at 17:04
98

ErrorResponse is your custom response object

Kotlin

val gson = Gson()
val type = object : TypeToken<ErrorResponse>() {}.type
var errorResponse: ErrorResponse? = gson.fromJson(response.errorBody()!!.charStream(), type)

Java

Gson gson = new Gson();
Type type = new TypeToken<ErrorResponse>() {}.getType();
ErrorResponse errorResponse = gson.fromJson(response.errorBody.charStream(),type);
Arshak
  • 3,197
  • 1
  • 25
  • 33
Shahab Rauf
  • 3,651
  • 29
  • 38
  • 6
    you shouldn't be force unwrapping optionals: `gson.fromJson(response.errorBody()?.charStream(), type)` – hopeman Mar 26 '19 at 10:11
46

I solved it by:

 if(!response.isSuccessful()){
       Gson gson = new Gson();
       MyErrorMessage message=gson.fromJson(response.errorBody().charStream(),MyErrorMessage.class);
       if(message.getCode()==ErrorCode.DUPLICATE_EMAIL_ID_CODE){
                  //DO Error Code specific handling                        
        }else{
                 //DO GENERAL Error Code Specific handling                               
        }
    }

MyErrorMessage Class:

  public class MyErrorMessage {
     private int code;
     private String message;

     public int getCode() {
        return code;
     }

     public void setCode(int code) {
        this.code = code;
     }

     public String getMessage() {
         return message;
     }

     public void setMessage(String message) {
        this.message = message;
     }
   }
Amarjit
  • 4,327
  • 2
  • 34
  • 51
Pooja Gupta
  • 461
  • 4
  • 2
37

It's actually very straight forward.

Kotlin:

val jsonObj = JSONObject(response.errorBody()!!.charStream().readText())
responseInterface.onFailure(jsonObj.getString("msg"))

Java:

    if(response.errorBody()!=null){
    JSONObject jsonObj = new JSONObject(TextStreamsKt.readText(response.errorBody().charStream()));
        responseInterface.onFailure(jsonObj.getString("msg"));
    }else{
        responseInterface.onFailure("you might want to return a generic error message.");
    }

Tested on retrofit:2.5.0. Read the text from the charStream which will give you a String, then parse to JSONObject.

Adios.

Wale
  • 1,644
  • 15
  • 31
  • 2
    there's no `readText()` extension on java, use `TextStreamsKt.readText(response.errorBody().charStream())` if you still on java – mochadwi Jun 05 '20 at 04:52
  • Update: Retrofit 2.6.2 -> `val errorMessage=jsonObj.getJSONArray("errors").getJSONObject(0).getString("message")` – Darshan Miskin Aug 15 '20 at 15:01
  • @Wale, Why this doesn't work for body without an error? **JSONObject(response.body()!!.charStream().readText())** – J A S K I E R Jan 26 '21 at 09:04
  • 1
    @Oleksandr I'm not sure I get your question but I don't think your response.body has a byteStream function, it could come as a String if you using a scala converter or related or it could simple come as an object you passed if you're using google gson converter. So, what you're trying to do might be this=> JSONObject (response.errorBody()!!.charStream().readText()) – Wale Jan 26 '21 at 14:58
  • @TaslimOseni I'm glad it worked for you too. – Wale Feb 16 '21 at 16:52
  • Works great! Thanks a lot. – Damercy Mar 26 '21 at 06:01
33

In Retrofit 2.0 beta2 this is the way that I'm getting error responses:

  1. Synchronous

    try {
       Call<RegistrationResponse> call = backendServiceApi.register(data.in.account, data.in.password,
               data.in.email);
       Response<RegistrationResponse> response = call.execute();
       if (response != null && !response.isSuccess() && response.errorBody() != null) {
           Converter<ResponseBody, BasicResponse> errorConverter =
                   MyApplication.getRestClient().getRetrofitInstance().responseConverter(BasicResponse.class, new Annotation[0]);
           BasicResponse error = errorConverter.convert(response.errorBody());
           //DO ERROR HANDLING HERE
           return;
       }
       RegistrationResponse registrationResponse = response.body();
       //DO SUCCESS HANDLING HERE
    } catch (IOException e) {
       //DO NETWORK ERROR HANDLING HERE
    }
    
  2. Asynchronous

    Call<BasicResponse> call = service.loadRepo();
    call.enqueue(new Callback<BasicResponse>() {
        @Override
        public void onResponse(Response<BasicResponse> response, Retrofit retrofit) {
            if (response != null && !response.isSuccess() && response.errorBody() != null) {
                Converter<ResponseBody, BasicResponse> errorConverter =
                    retrofit.responseConverter(BasicResponse.class, new Annotation[0]);
                BasicResponse error = errorConverter.convert(response.errorBody());
                //DO ERROR HANDLING HERE
                return;
            }
            RegistrationResponse registrationResponse = response.body();
            //DO SUCCESS HANDLING HERE
        }
    
        @Override
        public void onFailure(Throwable t) {
            //DO NETWORK ERROR HANDLING HERE
        }
    });
    

Update for Retrofit 2 beta3:

  1. Synchronous - not changed
  2. Asynchronous - Retrofit parameter was removed from onResponse

    Call<BasicResponse> call = service.loadRepo();
    call.enqueue(new Callback<BasicResponse>() {
        @Override
        public void onResponse(Response<BasicResponse> response) {
            if (response != null && !response.isSuccess() && response.errorBody() != null) {
                Converter<ResponseBody, BasicResponse> errorConverter =
                    MyApplication.getRestClient().getRetrofitInstance().responseConverter(BasicResponse.class, new Annotation[0]);
                BasicResponse error = errorConverter.convert(response.errorBody());
                //DO ERROR HANDLING HERE
                return;
            }
            RegistrationResponse registrationResponse = response.body();
            //DO SUCCESS HANDLING HERE
        }
    
        @Override
        public void onFailure(Throwable t) {
            //DO NETWORK ERROR HANDLING HERE
        }
    });
    
JFreeman
  • 685
  • 5
  • 16
29

Create a model of the Error response & user Gson to convert the response to it. This will just work fine.

APIError.java

public class APIError {
    private String message;

    public String getMessage() {
        return message;
    }
}

MainActivity.java (inside request onResponse)

if (response.isSuccessful()) {
    // Do your success stuff...

} else {
    APIError message = new Gson().fromJson(response.errorBody().charStream(), APIError.class);
    Toast.makeText(MainActivity.this, "" + message.getMessage(), Toast.LENGTH_SHORT).show();
}
Sreekant Shenoy
  • 1,420
  • 14
  • 23
12

If you use Kotlin another solution could be just create extension function for Response class:

inline fun <reified T>Response<*>.parseErrJsonResponse(): T?
{
    val moshi = MyCustomMoshiBuilder().build()
    val parser = moshi.adapter(T::class.java)
    val response = errorBody()?.string()
    if(response != null)
        try {
            return parser.fromJson(response)
        } catch(e: JsonDataException) {
            e.printStackTrace()
        }
    return null
}

Usage

val myError = response.parseErrJsonResponse<MyErrorResponse>()
if(myError != null) {
   // handle your error logic here
   // ...
}
Arsenius
  • 4,972
  • 4
  • 26
  • 39
11
 @Override
 public void onResponse(Call<Void> call, retrofit2.Response<Void> response) {
            if (response.isSuccessful()) {

            //Do something if response is ok
            } else {

                JsonParser parser = new JsonParser();
                JsonElement mJson = null;
                try {
                    mJson = parser.parse(response.errorBody().string());
                    Gson gson = new Gson();
                    MyError errorResponse = gson.fromJson(mJson, MyError.class);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }

            }
Vins
  • 385
  • 4
  • 9
10

In https://stackoverflow.com/a/21103420/2914140 and https://futurestud.io/tutorials/retrofit-2-simple-error-handling this variant is shown for Retrofit 2.1.0.

call.enqueue(new Callback<MyResponse>() {
    @Override
    public void onResponse(Call<MyResponse> call, Response<MyResponse> response) {
        if (response.isSuccessful()) {
            ...
        } else {
            Converter<ResponseBody, MyError> converter
                    = MyApplication.getRetrofit().responseBodyConverter(
                    MyError.class, new Annotation[0]);
            MyError errorResponse = null;
            try {
                errorResponse = converter.convert(response.errorBody());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
Community
  • 1
  • 1
CoolMind
  • 26,736
  • 15
  • 188
  • 224
7

I was facing same issue. I solved it with retrofit. Let me show this...

If your error JSON structure are like

{
"error": {
    "status": "The email field is required."
}
}


My ErrorRespnce.java 

public class ErrorResponse {

   @SerializedName("error")
   @Expose
   private ErrorStatus error;

   public ErrorStatus getError() {
      return error;
   }

   public void setError(ErrorStatus error) {
      this.error = error;
   }
}

And this my Error status class

public class ErrorStatus {

  @SerializedName("status")
  @Expose
  private String status;

  public String getStatus() {
      return status;
  }

  public void setStatus(String status) {
      this.status = status;
  }
}

Now we need a class which can handle our json.

  public class ErrorUtils {

   public static ErrorResponse parseError (Response<?> response){
      Converter<ResponseBody , ErrorResponse> converter =          ApiClient.getClient().responseBodyConverter(ErrorResponse.class , new Annotation[0]);
    ErrorResponse errorResponse;
    try{
        errorResponse = converter.convert(response.errorBody());
    }catch (IOException e){
        return new ErrorResponse();
    }
    return errorResponse;
}
}

Now we can check our response in retrofit api call

private void registrationRequest(String name , String email , String password , String c_password){


    final Call<RegistrationResponce> registrationResponceCall = apiInterface.getRegistration(name , email , password , c_password);
    registrationResponceCall.enqueue(new Callback<RegistrationResponce>() {
        @Override
        public void onResponse(Call<RegistrationResponce> call, Response<RegistrationResponce> response) {



            if (response.code() == 200){


            }else if (response.code() == 401){


                ErrorResponse errorResponse = ErrorUtils.parseError(response);
                Toast.makeText(MainActivity.this, ""+errorResponse.getError().getStatus(), Toast.LENGTH_SHORT).show();
            }
        }

        @Override
        public void onFailure(Call<RegistrationResponce> call, Throwable t) {

        }
    });
}

That's it now you can show your Toast

Ali Noureddine
  • 324
  • 5
  • 20
pavel
  • 1,603
  • 22
  • 19
7
if(!response.isSuccessful()) {
    StringBuilder error = new StringBuilder();
    try {
        BufferedReader bufferedReader = null;
        if (response.errorBody() != null) {
            bufferedReader = new BufferedReader(new InputStreamReader(
                    response.errorBody().byteStream()));

            String eLine = null;
            while ((eLine = bufferedReader.readLine()) != null) {
                error.append(eLine);
            }
            bufferedReader.close();
        }

    } catch (Exception e) {
        error.append(e.getMessage());
    }

    Log.e("Error", error.toString());
}
Riyas PK
  • 3,147
  • 1
  • 23
  • 29
7

There are many valid answers already. This is just an addition for a use case, when you need to consume same Retrofit response more than once. Neither of below can be used, as you can read response body only once, as it will be closed afterwards and you will get null each next time, when you try to read from the same response object:

response()?.errorBody()?.charStream()?.readText()
response()?.errorBody()?.string()

Instead, you can get read-only copy of response string (while the response itself can be passed over and eventually consumed later):

response()?.errorBody()?.source()?.buffer?.snapshot()?.utf8()
Myroslav
  • 896
  • 12
  • 21
6

I did it this way for asynchronous calls using Retrofit 2.0-beta2:

@Override
public void onResponse(Response<RegistrationResponse> response, 
                       Retrofit retrofit) {
    if (response.isSuccess()) {
        // Do success handling here
    } else {
        try {
            MyError myError = (MyError)retrofit.responseConverter(
                    MyError.class, MyError.class.getAnnotations())
                .convert(response.errorBody());
            // Do error handling here
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
JJD
  • 50,076
  • 60
  • 203
  • 339
Shantanu
  • 692
  • 5
  • 20
6

Here is elegant solution using Kotlin extensions:

data class ApiError(val code: Int, val message: String?) {
    companion object {
        val EMPTY_API_ERROR = ApiError(-1, null)
    }
}

fun Throwable.getApiError(): ApiError? {
    if (this is HttpException) {
        try {
            val errorJsonString = this.response()?.errorBody()?.string()
            return Gson().fromJson(errorJsonString, ApiError::class.java)
        } catch (exception: Exception) {
            // Ignore
        }
    }
    return EMPTY_API_ERROR
}

and usage:

showError(retrofitThrowable.getApiError()?.message)

Antonis Radz
  • 3,036
  • 1
  • 16
  • 34
  • Love this. Could also easily be made generic so that you could pass in an error type: `fun Throwable.getCustomException(classType: Class): T?` then update the GSON line to `Gson().fromJson(errorJsonString, classType)`. Used as `e.getCustomException(CustomException::class.java)?` – Bueno Aug 19 '20 at 15:48
3

This way you do not need a Retrofit instance if you only are injecting a service created from Retrofit.

public class ErrorUtils {

  public static APIError parseError(Context context, Response<?> response) {

    APIError error = new APIError();

    try {
        Gson gson = new Gson();
        error = gson.fromJson(response.errorBody().charStream(), APIError.class);
    } catch (Exception e) {
        Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show();
    }

    if (TextUtils.isEmpty(error.getErrorMessage())) {
        error.setError(response.raw().message());
    }
    return error;
  }
}

Use it like this:

if (response.isSuccessful()) {

      ...

    } else {

      String msg = ErrorUtils.parseError(fragment.getActivity(), response).getError(); // would be from your error class
      Snackbar.make(someview, msg, Snackbar.LENGTH_LONG).show();
    }
  }
Codeversed
  • 9,287
  • 3
  • 43
  • 42
3

if your error response is a string you can deserialize it by using the following kotlin code :

val errorString = response.errorBody()?.byteStream()?.bufferedReader().use { it?.readText() }  // defaults to UTF-8
a0x2
  • 1,995
  • 1
  • 18
  • 26
  • you can simply use "response.message().toString()" which will give the same error string in a more readable format. – Arpit Patel May 25 '22 at 19:51
3

json response

{
    "success": false,
    "status_code": 32,
    "status_message": "Email not verified: Your email address has not been verified."
}

Error class

data class ResponseError(
    @SerializedName("status_code")
    val statusCode: Int,
    @SerializedName("status_message")
    val statusMessage: String,
    @SerializedName("success")
    val success: Boolean
)

get error message

fun <T : Any> getResultOrError(response: Response<T>): T? {
    if (response.isSuccessful) {
        return response.body()
    } else {
        try {
            val responseError = Gson().fromJson(
                response.errorBody()?.string(),
                ResponseError::class.java
            )
            throw Throwable(responseError.statusMessage)
        } catch (e: Exception) {
            throw Throwable("Unknown error")
        }
    }
}
2

This seems to be the problem when you use OkHttp along with Retrofit, so either you can remove OkHttp or use code below to get error body:

if (!response.isSuccessful()) {
 InputStream i = response.errorBody().byteStream();
 BufferedReader r = new BufferedReader(new InputStreamReader(i));
 StringBuilder errorResult = new StringBuilder();
 String line;
 try {
   while ((line = r.readLine()) != null) {
   errorResult.append(line).append('\n');
   }
 } catch (IOException e) { 
    e.printStackTrace(); 
}
}
KRUPEN GHETIYA
  • 211
  • 2
  • 6
2

Tested and works

 public BaseModel parse(Response<BaseModel> response , Retrofit retrofit){
            BaseModel error = null;
            Converter<ResponseBody, BaseModel> errorConverter =
                    retrofit.responseBodyConverter(BaseModel.class, new Annotation[0]);
            try {
                if (response.errorBody() != null) {
                    error = errorConverter.convert(response.errorBody());
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return error;
        }
The MJ
  • 453
  • 7
  • 17
2

For people using Kotlin with Moshi and coroutines, this is what I did:

Error data class

@JsonClass(generateAdapter = true)
data class ApiResponseNoData(
    val exito: Int,
    val error: String?
)

Extension

fun ResponseBody.getApiError(): String? {
    return try {
        Moshi
            .Builder()
            .build()
            .adapter(ApiResponseNoData::class.java)
            .fromJson(string())
            ?.error
    }catch(e: Exception) { null }
}

ViewModel

fun test() {
    viewModelScope.launch(Dispatchers.IO) {
        val response = repository.test()
        withContext(Dispatchers.Main) {
            if(response.isSuccessful) {
                ...
            }else{
                val errorMsg = response.errorBody()?.getApiError() ?: "Unexpected error occurred"
                ...
            ]
        }
    }
}
Ricardo Yubal
  • 374
  • 3
  • 8
1

In Kotlin I solved it creating a custom ResponseBody generic extension function function that converts the response body to a JSONObject. then you can use gson to customize the error response body with your custom Error Data Class.

inline fun <reified T> ResponseBody.getErrorObject(): T {
    val gson = Gson()
    val jsonObject = JSONObject(charStream().readText())
    return gson.fromJson(jsonObject.toString(), T::class.java)
}

You can then customize the error response to your custom class. For this I'm using an example

data class LoginError(
    val error: Error,
    val message: String,
    val success: Boolean
)

data class Error(
    val error: String,
    val status: Int
)

then use the extension function this way

val error = state.errorBody.getErrorObject<LoginError>()

the state.errorBody is my error response from retrofit of type ResponseBody

0

solved it by:

Converter<MyError> converter = 
    (Converter<MyError>)JacksonConverterFactory.create().get(MyError.class);
MyError myError =  converter.fromBody(response.errorBody());
JJD
  • 50,076
  • 60
  • 203
  • 339
Piotr Boho
  • 2,650
  • 2
  • 13
  • 20
0
try{
                ResponseBody response = ((HttpException) t).response().errorBody();
                JSONObject json = new JSONObject( new String(response.bytes()) );
                errMsg = json.getString("message");
            }catch(JSONException e){
                return t.getMessage();
            }
            catch(IOException e){
                return t.getMessage();
            }
Mike6679
  • 5,547
  • 19
  • 63
  • 108
0

In Kotlin:

val call = APIClient.getInstance().signIn(AuthRequestWrapper(AuthRequest("1234567890z", "12341234", "nonce")))
call.enqueue(object : Callback<AuthResponse> {
    override fun onResponse(call: Call<AuthResponse>, response: Response<AuthResponse>) {
        if (response.isSuccessful) {

        } else {
            val a = object : Annotation{}
            val errorConverter = RentalGeekClient.getRetrofitInstance().responseBodyConverter<AuthFailureResponse>(AuthFailureResponse::class.java, arrayOf(a))
            val authFailureResponse = errorConverter.convert(response.errorBody())
        }
    }

    override fun onFailure(call: Call<AuthResponse>, t: Throwable) {
    }
})
Adam Johns
  • 35,397
  • 25
  • 123
  • 176
0

errorBody values should set APIError object in Retrofit. So that, you can use the below code structure.

public class APIErrorUtils {

    public static APIError parseError(Response<?> response) {
        Converter<ResponseBody, APIError> converter = API.getClient().responseBodyConverter(APIError.class, new Annotation[0]);

        APIError error;

        try {
            error = converter.convert(response.errorBody());
            Log.d("SERVICELOG", "****************************************************");
            Log.d("SERVICELOG", "***** SERVICE LOG");
            Log.d("SERVICELOG", "***** TIMESTAMP: " + String.valueOf(error.getTimestamp()));
            Log.d("SERVICELOG", "***** STATUS: " + String.valueOf(error.getStatus()));
            Log.d("SERVICELOG", "***** ERROR: " + error.getError());
            Log.d("SERVICELOG", "***** MESSAGE: " + error.getMessage());
            Log.d("SERVICELOG", "***** PATH: " + error.getPath());
            Log.d("SERVICELOG", "****************************************************");
        } catch (IOException e) {
            return new APIError();
        }

        return error;
    }
}

APIError error = APIErrorUtils.parseError(response);
if (error.getStatus() == 400) {
    ....
}
Mehmed
  • 2,880
  • 4
  • 41
  • 62
Egemen Mede
  • 191
  • 3
0
val error = JSONObject(callApi.errorBody()?.string() as String)
            CustomResult.OnError(CustomNotFoundError(userMessage = error["userMessage"] as String))

open class CustomError (
    val traceId: String? = null,
    val errorCode: String? = null,
    val systemMessage: String? = null,
    val userMessage: String? = null,
    val cause: Throwable? = null
)

open class ErrorThrowable(
    private val traceId: String? = null,
    private val errorCode: String? = null,
    private val systemMessage: String? = null,
    private val userMessage: String? = null,
    override val cause: Throwable? = null
) : Throwable(userMessage, cause) {
    fun toError(): CustomError = CustomError(traceId, errorCode, systemMessage, userMessage, cause)
}


class NetworkError(traceId: String? = null, errorCode: String? = null, systemMessage: String? = null, userMessage: String? = null, cause: Throwable? = null):
    CustomError(traceId, errorCode, systemMessage, userMessage?: "Usted no tiene conexión a internet, active los datos", cause)

class HttpError(traceId: String? = null, errorCode: String? = null, systemMessage: String? = null, userMessage: String? = null, cause: Throwable? = null):
    CustomError(traceId, errorCode, systemMessage, userMessage, cause)

class UnknownError(traceId: String? = null, errorCode: String? = null, systemMessage: String? = null, userMessage: String? = null, cause: Throwable? = null):
    CustomError(traceId, errorCode, systemMessage, userMessage?: "Unknown error", cause)

class CustomNotFoundError(traceId: String? = null, errorCode: String? = null, systemMessage: String? = null, userMessage: String? = null, cause: Throwable? = null):
    CustomError(traceId, errorCode, systemMessage, userMessage?: "Data not found", cause)`
0

Error body handling in kotlin Android

catch (cause: Throwable) {
            when (cause) {
                is HttpException -> {
                    try {
                        val YourErrorResponseClassObj = Gson().fromJson(cause.response()?.errorBody()?.charStream(), YourErrorResponseClass::class.java)
                    } catch (e: Exception) {
                        
                    }
                }
                else -> {
                    //Other errors like Network ...
                }
            }
        }
Abhishek Garg
  • 3,092
  • 26
  • 30
0

very simple. and this save my life ever

public static void displayApiResponseErrorBody(Response<?> response)
{
    InputStream i = response.errorBody().byteStream();
    BufferedReader r = new BufferedReader(new InputStreamReader(i));
    StringBuilder errorResult = new StringBuilder();
    String line;
    try {
        while ((line = r.readLine()) != null) 
        {
            errorResult.append(line).append('\n');
        }
        Log.d("API_RESPONSE_ERROR_BODY",String.valueOf(errorResult));
        System.out.println(errorResult);
    } catch (IOException e) {
        e.printStackTrace();
    }
}
hamil.Dev
  • 137
  • 2
  • 9
0

In case of retrofit error Response, You can get body using error.getResponse(), Here is the example.

        @Override
        public void failure(RetrofitError error){
            if(error.getResponse().getStatus()==201){
                LogUtil.INSTANCE.debug("Success : " + error.toString());
                callback.success(error.getResponse().getBody);
            }else{
                LogUtil.INSTANCE.debug("failure: " + error.toString());
                callback.failure(error);
            }
        }
Farid Haq
  • 3,728
  • 1
  • 21
  • 15
0
val reader = BufferedReader(response.errorBody()?.source().inputStream().reader())
                
val content = StringBuilder()

reader.use { readerBuffer ->
    var line = readerBuffer.readLine()
    while (line != null) {
        content.append(line)
        line = readerBuffer.readLine()
    }
}

Gson().fromJson(content.toString(), ResponseData::class.java)
Procrastinator
  • 2,526
  • 30
  • 27
  • 36
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Jonas Aug 23 '22 at 07:29
-1

I did this way and it worked like a charm

val errorBody = (response?.errorBody() as ResponseBody).string()
Atif AbbAsi
  • 5,633
  • 7
  • 26
  • 47