Can`t solve "Content-Length and stream length disagree" error - android

I get an error while trying to execute the following code:
fun parseErrorCodes(response: Response<*>): List<String> {
val errorCodes: MutableList<String> = ArrayList()
try {
val listType = object : TypeToken<ArrayList<ApiError>>() {
}.type
val errorJson = JsonParser().parse(response.errorBody().string()).asJsonObject.get("response")
if (errorJson.isJsonArray) {
val errors = Gson().fromJson<List<ApiError>>(errorJson, listType)
for (apiError in errors) {
errorCodes.add(apiError.errorCode)
}
return errorCodes
} else {
errorCodes.add(Gson().fromJson(errorJson, ApiError::class.java).errorCode)
return errorCodes
}
} catch (e: Exception) {
e.printStackTrace()
}
return errorCodes
}
The error occurs at the line : val errorJson = JsonParser().parse(response.errorBody().string()).asJsonObject.get("response")
Can someone help me to solve this error?

I found the answer to my question. The problem was that I was trying to parse the response for the API twice, first time to show the error messages and then to get the error codes to handle them for future validations.
This is how my code looks:
ErrorHandler.showError(activity, response)
val errorCodes = ErrorHandler.parseErrorCodes(response)
handleErrorCodes(errorCodes)
So, both methods showError and parseErrorCodes were working with the API response.

Related

I keep getting the error "E/Network: searchBooks: Failed Getting books" but I am not sure why

So I am using the Google's API and for some reason, I'm getting a generic error:
E/Network: searchBooks: Failed Getting books
When it initially loads up, the hard coded query "android" shows up with a list of books associated with the book topic. But when I search up a different topic like "shoes" for example, the error shows up. Even when you hard code a different topic other than "android", it still shows the error. I have checked the API and it is working properly with the different query searches.
Here's the Retrofit Interface:
#Singleton
interface BooksApi {
#GET(BOOK_EP)
suspend fun getAllBooks(
//don't initialize the query, so that the whole api is available to the user
#Query("q") query: String
): Book
#GET("$BOOK_EP/{bookId}")
suspend fun getBookInfo(
#Path("bookId") bookId: String
): Item
}
The Repo
class BookRepository #Inject constructor(private val api: BooksApi) {
suspend fun getBooks(searchQuery: String): Resource<List<Item>> {
return try {
Resource.Loading(data = true)
val itemList = api.getAllBooks(searchQuery).items
if(itemList.isNotEmpty()) Resource.Loading(data = false)
Resource.Success(data = itemList)
}catch (exception: Exception){
Resource.Error(message = exception.message.toString())
}
}
suspend fun getBookInfo(bookId: String): Resource<Item>{
val response = try {
Resource.Loading(data = true)
api.getBookInfo(bookId)
}catch (exception: Exception){
return Resource.Error(message = "An error occurred ${exception.message.toString()}")
}
Resource.Loading(data = false)
return Resource.Success(data = response)
}
The ViewModel:
class SearchViewModel #Inject constructor(private val repository: BookRepository): ViewModel(){
var list: List<Item> by mutableStateOf(listOf())
var isLoading: Boolean by mutableStateOf(true)
init {
loadBooks()
}
private fun loadBooks() {
searchBooks("android")
}
fun searchBooks(query: String) {
viewModelScope.launch(Dispatchers.Default) {
if (query.isEmpty()){
return#launch
}
try {
when(val response = repository.getBooks(query)){
is Resource.Success -> {
list = response.data!!
if (list.isNotEmpty()) isLoading = false
}
is Resource.Error -> {
isLoading = false
Log.e("Network", "searchBooks: Failed Getting books", )
}
else -> {isLoading = false}
}
}catch (exception: Exception){
isLoading = false
Log.d("Network", "searchBooks: ${exception.message.toString()}")
}
}
}
}
I'll leave the project public so you guys can check it out for more of an understanding
Github Link: https://github.com/OEThe11/ReadersApp
P.S. you would have to create a login (takes 30 sec), but once you do, you'll have access to the app immediately.
This issue is occurring because of JsonSyntaxException java.lang.NumberFormatException while the JSON response is getting parsed from the API. This is because the averageRating field in the VolumeInfo data class is declared as Int but the response can contain floating point values.
If you change averageRating field type from Int to Double in the VolumeInfo data class, the exception would no longer occur.
I suggest you to debug your code in such cases.

Type mismatch: inferred type is Unit when Weather was expected

I am working on an Android Weather application and I am getting the error described in the title. The function that is causing this error is a repository that is calling some weather data. I have a helper class called DataOrException which is:
class DataOrException<T, Boolean, E>(
var data: T? = null,
var loading: Kotlin.Boolean? = null,
var e: E? = null
)
The function that is calling this class is a coroutine that is getting the weather information from repository which is using Injection to return the class. Here's the function:
suspend fun getWeather(cityQuery: String, units: String): DataOrException<Weather, Boolean, Exception> {
val response = try {
api.getWeather(query = cityQuery, units = units)
} catch (e: Exception) {
Log.d("REX", "getWeather: $e")
return DataOrException(e = e)
}
return DataOrException(data = response) //Error occurs here.
Any ideas on how to fix this error?
Your getWeather function in WeatherApi is not returning anything so it's basically a kotlin Unit. But, In your repository return DataOrException(data = response) here data is expected to be of type Weather. That's why the error.
Solution:
Return Weather from WeatherApi function getWeather & keep everything else as it was.
interface WeatherApi {
#GET(value = "/cities/cityID=Chelsea")
suspend fun getWeather(#Query("q") query: String, #Query("units") units: String = "imperial") : Weather
}
OR
================
Change data type to Unit by changing to : DataOrException<Unit, Boolean, Exception>
suspend fun getWeather(
cityQuery: String,
units: String
): DataOrException<Unit, Boolean, Exception> {
val response = try {
api.getWeather(query = cityQuery, units = units)
} catch (e: Exception) {
Log.d("REX", "getWeather: $e")
return DataOrException(e = e)
}
return DataOrException(data = response)
}

Retain errorBody from HttpException

I am trying to map the error body from an exception into into a useful model with now luck, using moshi and retrofit 2.9.0
I found numerous posts discussing the same issue but none of the solutions worked for me.
My code is the following
private fun getErrorMessageFromGenericResponse(httpException: HttpException): String? {
var errorMessage: String? = null
try {
val body = httpException.response()!!.errorBody()!!
val errorResponse = body.string().toObject(ErrorResponse::class.java)
errorMessage = errorResponse?.message
} catch (e: IOException) {
e.printStackTrace()
} finally {
return errorMessage
}
}
fun <T> String.toObject(objectClass: Class<T>): T? {
val moshi = Moshi.Builder().build()
val adapter = moshi.adapter(objectClass).lenient()
return adapter.fromJson(this)
}
I tried also using this but it also does not work:
httpException.response()!!.errorBody()!!.source().buffer.snapshot().utf8()
I am probably missing something really simple as I think its a common usecase.
Thanks in advance.
fun handleErrorResponse(e: Throwable): String {
return when (e) {
is HttpException -> {
parseHTTPError(e.response()!!.errorBody())
}
is SocketTimeoutException -> {
ApiConstants.TIME_OUT
}
is IOException -> {
ApiConstants.SERVERERROR
}
else -> ApiConstants.SERVERERROR
}
}
fun parseHTTPError(responseBody: ResponseBody?): String {
try {
val jsonObject=JSONObject(responseBody!!.string())
try {
val error=jsonObject.getJSONArray("message")
return error[0].toString()
}
catch (ex: Exception) {
responseBody!!.close()
return ""
}
responseBody.close()
return ""
}

retrofit - kotlin - Parameter specified as non-null is null

I'm using mvvm , kotlin , retrofit and courtin in my app . I've done several request and all of them works fine but with this one , I get this error "Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter list"
this is my json
{
"roomslist": [
{
"id": "1"
}
]
}
these are my models
data class RoomsListModel(
#Json(name = "roomslist")
val roomsList: List<Rooms>
)
data class Rooms(
#Json(name = "id")
val id: String
}
this is my api interface :
#FormUrlEncoded
#POST("getPlaceRooms.php")
fun getPlaceRooms2(#Field("amlakid")id:String):Deferred<RoomsListModel>
this is my repository :
fun getRooms(
amlakId: String
): MutableLiveData<RoomsListModel> {
scope.launch {
val request = api.getPlaceRooms2(amlakId)
withContext(Dispatchers.Main) {
try {
val response = request.await()
roomsLiveData.value = response
} catch (e: HttpException) {
Log.v("this", e.message);
} catch (e: Throwable) {
Log.v("this", e.message);
}
}
}
return roomsLiveData;
}
when the app runs , it goes into e: Throwable and returns the error
my viewmodel
class PlacesDetailsViewModel : ViewModel() {
private val repository = PlaceDetailsRepository()
fun getRooms(amlakId: String):MutableLiveData<RoomsListModel> {
return repository.getRooms(amlakId)
}
}
and this my activity request :
viewModel.getRooms(amlakId).observe(this, Observer {
vf.frm_loading.visibility = View.GONE
it?.let {
adapter.updateList(it?.roomsList)
setNoItem(false)
}
})
I'm using moshi
I've tried to clean ,rebuild but it doesn't make any different
could you help me ?
what is going wrong with my code?
You should try adding ? to your Model parameters. Not sure if in your case is the String?. It will ensure that you can have null values on your String
val id: String?
Please double check, whatever value is missing or null in your case
Have you tried removing #Json annotation in your val id: String declaration?

Unable to get error response in error body of retrofit

I am unable to get 400 response in error body of retrofit. I have set logging level its showing in logs but not showing in error body i have searched a lot but didn't find any solution is anyone there who help me in this case to get rid of this problem
call_.enqueue(object : Callback<ResponseBody> {
override fun onResponse(call: Call<ResponseBody>?, response: Response<ResponseBody>?) {
if (response?.code() == 400) {
var jObjError: JSONObject? = null
try {
var jObjErrorr = response.errorBody().string()
CustomLogs.displayLogs("$TAG jObjErrorr: $jObjErrorr")
} catch (e: Exception) {
}
try {
val string = jObjError?.getstring("error_description")
CustomLogs.displayLogs("$TAG jObjError: $string")
} catch (e: Exception) {
e.printStackTrace();
}
}
}
i need error body to get and display message and my log shows this
{"error":"Authorize","error_description":"Error in authentication"}
but error body is not showing this object
As IntelliJ Amiya mentioned in comment to your original post you should do this in onFailure method. As far as I know Retrofit's onResponse will not be called in cases of response code not in 200 range (200, 201, 202 etc.) so your check for if (response?.code() == 400) will never return true.
If you go through the Retrofit onResponse Library...,it's clearly mentioned that Retrofit does not create Body for response with status code below 200 or above 300.You have to specify your error Response Specifically!!
Decided to add it as separate answer:
if (response?.code() == 400) {
var jObjError: JSONObject? = null
try {
jObjError = response.errorBody().string()
CustomLogs.displayLogs("$TAG jObjError: $jObjError")
} catch (e: Exception) {
}
try {
val string = jObjError?.optString("error_description")
CustomLogs.displayLogs("$TAG jObjError: $string")
} catch (e: Exception) {
e.printStackTrace();
}
}
Could you try this fragment?
you can do this in Kotlin:
val errorResponse: ErrorMessage? = Gson().fromJson(
response.errorBody()!!.charStream(),
object : TypeToken<ErrorMessage>() {}.type
)

Categories

Resources