After updating to retrofit beta-3 I'm getting the Exception (Because of an empty body)
java.io.EOFException: End of input at line 1 column 1
at com.google.gson.stream.JsonReader.nextNonWhitespace(JsonReader.java:1414)
at com.google.gson.stream.JsonReader.doPeek(JsonReader.java:553)
at com.google.gson.stream.JsonReader.peek(JsonReader.java:429)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:202)
at com.google.gson.TypeAdapter.fromJson(TypeAdapter.java:260)
at retrofit2.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:33)
at retrofit2.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:23)
at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:154)
at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:92)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:133)
I know that It's possible to solve this issue Using Call<Void> but is there any other way to enforce OkHttp or Gson To accept Empty body?
Response Log:
D/OkHttp: Date: Mon, 01 Feb 2016 08:32:10 GMT
D/OkHttp: Server: Apache/2.4.7 (Ubuntu)
D/OkHttp: X-Powered-By: PHP/5.5.9-1ubuntu4.13
D/OkHttp: Expires: Thu, 19 Nov 1981 08:52:00 GMT
D/OkHttp: Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
D/OkHttp: Pragma: no-cache
D/OkHttp: Access-Control-Allow-Origin: https://example.com
D/OkHttp: Access-Control-Allow-Methods: GET,POST,OPTIONS
D/OkHttp: Access-Control-Allow-Headers: Accept,Cache-Control,Pragma,Origin,Authorization,Content-Type,X-Requested-With,Cookie,*
D/OkHttp: Access-Control-Allow-Credentials: true
D/OkHttp: Content-Length: 0
D/OkHttp: Keep-Alive: timeout=5, max=99
D/OkHttp: Connection: Keep-Alive
D/OkHttp: Content-Type: application/json
D/OkHttp: OkHttp-Sent-Millis: 1454315528548
D/OkHttp: OkHttp-Received-Millis: 1454315528725
D/OkHttp: <-- END HTTP (0-byte body)
Are you getting the same error when you don´t set a converter factory?
I didn't test without the converter. I guess it will not fail because GsonResponseBodyConverter.convert will not run. Is there any reason why i should check without the converter?
What behavior do you expect when the body is empty then?
You are telling Retrofit to deserialize the response as a certain object and Retrofit tells Gson to parse the stream into that type and the type is empty. This behavior already seems very reasonable to me.
Just return an "empty" pojo response (Like it was until now)... all fields null? It's a real problem...for example I didn't write the server side, just the Android App...The server side returns 200 with empty body...but there are times that the same call will return a 200 with a body message. I have nothing to do in situations like that
An empty pojo is {} in JSON.
You can write a delegating converter that does this, the behavior will not be added to Retrofit by default.
class NullOnEmptyConverterFactory implements Converter.Factory {
@Override public Converter<ResponseBody, ?> responseBody(Type type, Annotation[] annotations, Retrofit retrofit) {
final Converter<ResponseBody, ?> delegate = retrofit.nextResponseBodyConverter(this, type, annotations);
return new Converter<>() {
@Override public void convert(ResponseBody body) {
if (body.contentLength() == 0) return null;
return delegate.convert(body);
}
};
}
}
Retrofit retrofit = new Retrofit.Builder()
.endpoint(..)
.addConverterFactory(new NullOnEmptyConverterFactory())
.addConverterFactory(GsonConverterFactory.create())
.build();
I had to implement this for a requirement from my services team. Here is the updated code I used for release version of Retrofit 2.0 that seems to be working in case anyone else comes across the issue.
public class NullOnEmptyConverterFactory extends Converter.Factory {
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
final Converter<ResponseBody, ?> delegate = retrofit.nextResponseBodyConverter(this, type, annotations);
return new Converter<ResponseBody, Object>() {
@Override
public Object convert(ResponseBody body) throws IOException {
if (body.contentLength() == 0) return null;
return delegate.convert(body); }
};
}
}
body.contentLength() always returns -1 on me. What to do?
Just read it. That means unknown content length.
On Wed, May 11, 2016, 11:51 PM nAkhmedov [email protected] wrote:
body.contentLength() always returns -1 on me. What to do?
—
You are receiving this because you modified the open/close state.Reply to this email directly or view it on GitHub
https://github.com/square/retrofit/issues/1554#issuecomment-218674519
05-12 11:57:35.040 12039-13092/com.sms.sendsms D/OkHttp: <-- 200 OK http://www.yesplease.co.il/app/login.asp?username=zolim2&password=123456g (554ms)
05-12 11:57:35.041 12039-13092/com.sms.sendsms D/OkHttp: Cache-Control: must-revalidate,no-cache,private
05-12 11:57:35.041 12039-13092/com.sms.sendsms D/OkHttp: Content-Type: text/html; Charset=utf-8
05-12 11:57:35.041 12039-13092/com.sms.sendsms D/OkHttp: Expires: Wed, 11 May 2016 06:58:56 GMT
05-12 11:57:35.041 12039-13092/com.sms.sendsms D/OkHttp: Vary: Accept-Encoding
05-12 11:57:35.041 12039-13092/com.sms.sendsms D/OkHttp: Server: Microsoft-IIS/7.5
05-12 11:57:35.041 12039-13092/com.sms.sendsms D/OkHttp: X-Powered-By: Yes Please
05-12 11:57:35.041 12039-13092/com.sms.sendsms D/OkHttp: Server: Server
05-12 11:57:35.041 12039-13092/com.sms.sendsms D/OkHttp: Date: Thu, 12 May 2016 06:58:56 GMT
05-12 11:57:35.041 12039-13092/com.sms.sendsms D/OkHttp: Connection: Keep-Alive
05-12 11:57:35.041 12039-13092/com.sms.sendsms D/OkHttp: Set-Cookie: source=; path=/
05-12 11:57:35.041 12039-13092/com.sms.sendsms D/OkHttp: Set-Cookie: ASPSESSIONIDCSASTAQA=NCLNDGIBMFFIEGMOBHIABCEC; path=/
05-12 11:57:35.041 12039-13092/com.sms.sendsms D/OkHttp: OkHttp-Sent-Millis: 1463036254545
05-12 11:57:35.041 12039-13092/com.sms.sendsms D/OkHttp: OkHttp-Received-Millis: 1463036255040
05-12 11:57:35.041 12039-13092/com.sms.sendsms D/OkHttp: <-- END HTTP (0-byte body)
05-12 11:57:35.081 12039-12039/com.sms.sendsms W/System.err: java.io.EOFException: End of input at line 1 column 1
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();//If need to logging, just uncomment
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ContextConstants.APP_URL)
.addConverterFactory(new NullOnEmptyConverterFactory())
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
I add the NullOnEmptyConverterFactory to the retrofit, and still gets the error.Then I find the order is important.
Retrofit retrofit = new Retrofit.Builder()
.endpoint(..)
.addConverterFactory(new NullOnEmptyConverterFactory()) //this should come first
.addConverterFactory(GsonConverterFactory.create())
.build();
help for Kotlin devs
val nullOnEmptyConverterFactory = object : Converter.Factory() {
fun converterFactory() = this
override fun responseBodyConverter(type: Type, annotations: Array<out Annotation>, retrofit: Retrofit) = object : Converter<ResponseBody, Any?> {
val nextResponseBodyConverter = retrofit.nextResponseBodyConverter<Any?>(converterFactory(), type, annotations)
override fun convert(value: ResponseBody) = if (value.contentLength() != 0L) nextResponseBodyConverter.convert(value) else null
}
}
@jacklt That is an inefficient solution. You have to look up the converter for _every_ response. Look it up once in the factory so that it can be used over and over by the returned converter.
@JakeWharton Why? I create only one Converter.Factory(). What's wrong with that code?
It calls nextResponseBodyConverter for every response.
@jacklt You want to cache the result of nextResponseBodyConverter.
something like:
override fun responseBodyConverter(type: Type, annotations: Array<out Annotation>, retrofit: Retrofit): Converter<ResponseBody, *>? {
val nextResponseBodyConverter = retrofit.nextResponseBodyConverter<Any>(this, type, annotations)
return Converter<ResponseBody, Any> {
if (it.contentLength() != 0L) {
nextResponseBodyConverter.convert(it)
} else {
null
}
}
}
Ok, thanks! That's the full version
val nullOnEmptyConverterFactory = object : Converter.Factory() {
fun converterFactory() = this
override fun responseBodyConverter(type: Type, annotations: Array<out Annotation>, retrofit: Retrofit) = object : Converter<ResponseBody, Any?> {
val nextResponseBodyConverter = retrofit.nextResponseBodyConverter<Any?>(converterFactory(), type, annotations)
override fun convert(value: ResponseBody) = if (value.contentLength() != 0L) nextResponseBodyConverter.convert(value) else null
}
}
In Gson's fromJson implementation, it take care of empty JSON string, by catching the EOFException and returning null, its comment said:
For compatibility with JSON 1.5 and earlier, we return null for empty documents instead of throwing.
So could it be possible that GsonResponseBodyConverter apply the same logic?
Any way, the NullOnEmptyConverterFactory is a good solution.
No, we won't be implementing that. An empty body is not valid JSON.
On Sun, Aug 21, 2016 at 12:58 AM Piasy [email protected] wrote:
In Gson's fromJson implementation
https://github.com/google/gson/blob/master/gson/src/main/java/com/google/gson/Gson.java#L891,
it take care of empty JSON string, by catching the EOFException and
returning null, its comment said:For compatibility with JSON 1.5 and earlier, we return null for empty
documents instead of throwing.So could it be possible that GsonResponseBodyConverter apply the same
logic?Any way, the NullOnEmptyConverterFactory is a good solution.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/square/retrofit/issues/1554#issuecomment-241239117,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAEEEYMo8CG1Ae36ntGwnUxno5Gc4vISks5qh9sOgaJpZM4HQXza
.
@JakeWharton will it deal with empty arrays like "array":[] ?
That's up to the serialization library you are using.
I've got a good way to handle this, just want to share with you guys :)
I create an EmptyJsonLenientConverterFactory to deal with empty body:
public class EmptyJsonLenientConverterFactory extends Converter.Factory {
private final GsonConverterFactory mGsonConverterFactory;
public EmptyJsonLenientConverterFactory(GsonConverterFactory gsonConverterFactory) {
mGsonConverterFactory = gsonConverterFactory;
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return mGsonConverterFactory.requestBodyConverter(type, parameterAnnotations,
methodAnnotations, retrofit);
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
final Converter<ResponseBody, ?> delegateConverter =
mGsonConverterFactory.responseBodyConverter(type, annotations, retrofit);
return value -> {
try {
return delegateConverter.convert(value);
} catch (EOFException e) {
// just return null
return null;
}
};
}
}
And besides, my server return api error in HTTP 200 response, so I also create a YLApiErrorAwareConverterFactory to catch them:
public class YLApiErrorAwareConverterFactory extends Converter.Factory {
private final EmptyJsonLenientConverterFactory mEmptyJsonLenientConverterFactory;
public YLApiErrorAwareConverterFactory(
EmptyJsonLenientConverterFactory emptyJsonLenientConverterFactory) {
mEmptyJsonLenientConverterFactory = emptyJsonLenientConverterFactory;
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return mEmptyJsonLenientConverterFactory.requestBodyConverter(type, parameterAnnotations,
methodAnnotations, retrofit);
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
final Converter<ResponseBody, ?> apiErrorConverter =
mEmptyJsonLenientConverterFactory.responseBodyConverter(YLApiError.class,
annotations, retrofit);
final Converter<ResponseBody, ?> delegateConverter =
mEmptyJsonLenientConverterFactory.responseBodyConverter(type, annotations,
retrofit);
return value -> {
// read them all, then create a new ResponseBody for ApiError
// because the response body is wrapped, we can't clone the ResponseBody correctly
MediaType mediaType = value.contentType();
String stringBody = value.string();
try {
Object apiError = apiErrorConverter
.convert(ResponseBody.create(mediaType, stringBody));
if (apiError instanceof YLApiError && ((YLApiError) apiError).isApiError()) {
throw (YLApiError) apiError;
}
} catch (JsonSyntaxException notApiError) {
}
// then create a new ResponseBody for normal body
return delegateConverter.convert(ResponseBody.create(mediaType, stringBody));
};
}
}
And to wire them up:
Retrofit retrofit = new Retrofit.Builder().client(okHttpClient)
.baseUrl(URL)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(new YLApiErrorAwareConverterFactory(
new EmptyJsonLenientConverterFactory(GsonConverterFactory.create(gson))))
.build();
package com.dooioo.core.network;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;
/**
@Override
public Converter
final Converter
```
return new Converter
@Override
public Object convert(ResponseBody value) throws IOException {
if (value.contentLength() == 0) return null;
return delegate.convert(value);
}
};
```
}
}
//ConverterFactory
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type,
Annotation[] annotations,
Retrofit retrofit) {
final Converter<ResponseBody, ?> delegateConverter = // 3
mGsonConverterFactory.responseBodyConverter(type,
annotations, retrofit);
return new Converter<ResponseBody, Object>() {
@Override
public Object convert(ResponseBody value) throws IOException {
try {
return delegateConverter.convert(value); // 4
} catch (JsonSyntaxException e) {
// just return null
Timber.e("Json covert error -->error : " + e);
return ApiHelper.createErrorResult(); // return a errorResult
}
}
};
}
//crate errorResult
public static HttpResult createErrorResult() {
HttpResult httpResult = new HttpResult();
httpResult.setCode(DATA_ERROR);
return httpResult;
}
//hand error
@Override
public Observable<R> call(HttpResult<R> rHttpResult) {
if (rHttpResult == null) {
return Observable.error(new NetworkConnectionException());
} else if (ApiHelper.isDataError(rHttpResult)) {
return Observable.error(new ServerErrorException());//server data error
} else if (!ApiHelper.isSuccess(rHttpResult)) {
if (ApiHelper.isLoginExpired(rHttpResult)) {
RxBus.send(new EventLoginExpired());
}
return Observable.error(createException(rHttpResult));
} else {
return Observable.just(rHttpResult.getData());
}
}
The server I am talking to (which I do not control) does not return a valid content-length so solutions that rely on it do not work for me. Furthermore I am using kotlin and using moshi. This is the only solution I could get working. I realize this has a serious error that swallows EOFException but I do not see any other way of doing this. Any ideas?
class NullMoshiConverterFactory(val moshi: Moshi) : Converter.Factory() {
val factory: MoshiConverterFactory
init {
factory = MoshiConverterFactory.create(moshi)
}
override fun requestBodyConverter(type: Type?, parameterAnnotations: Array<out Annotation>?, methodAnnotations: Array<out Annotation>?, retrofit: Retrofit?): Converter<*, RequestBody> {
return factory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, retrofit)
}
override fun responseBodyConverter(type: Type, annotations: Array<out Annotation>, retrofit: Retrofit): Converter<ResponseBody, *>? {
return NullConverterFactory(factory.responseBodyConverter(type, annotations, retrofit))
}
class NullConverterFactory(val wrapped: Converter<ResponseBody, *>) : Converter<ResponseBody, Any> {
override fun convert(value: ResponseBody?): Any? {
try {
return wrapped.convert(value)
} catch (ex: EOFException) {
return null
}
}
}
}
E/UncaughtException: java.lang.IllegalArgumentException: Expected receiver of type com.XXXX.models.SuccessResponse, but got retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall
@nAkhmedov I met the same situation as you, how do you deal with it?
@SugerQ i tottaly forget what was the reason. Sorry
Blast from the past, but here is a fully anonymous impl:
Retrofit retrofit = new Retrofit.Builder()
...
//
// Prevent empty body from throwing "java.io.EOFException: End of input at line 1 column 1 path $"
// Per: https://github.com/square/retrofit/issues/1554
//
.addConverterFactory(new Factory()
{
@Nullable
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit)
{
final Converter<ResponseBody, Object> delegate = retrofit.nextResponseBodyConverter(this, type, annotations);
return new Converter<ResponseBody, Object>()
{
@Override
public Object convert(@NonNull ResponseBody body)
throws IOException
{
return body.contentLength() != 0 ? delegate.convert(body) : null;
}
};
}
})
...
.build();
Sadly, here's another 'me too'.
I've tried each and every one of these approaches, without success.
I'm creating my builder like this:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url.toString())
.client(client)
.addConverterFactory(new NullOnEmptyConverterFactory())
.addConverterFactory(converterFactory)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
This is in a static method, so I'm creating my NullConverter like this:
public static class NullOnEmptyConverterFactory extends Converter.Factory {
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
final Converter<ResponseBody, ?> delegate = retrofit.nextResponseBodyConverter(this, type, annotations);
Log.d(TAG, "responseBodyConverter: creating the NULL converter");
return new Converter<ResponseBody, Object>() {
@Override
public Object convert(ResponseBody body) throws IOException {
Log.d(TAG, "convert: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
if (body.contentLength() == 0) {
Log.d(TAG, "convert: returning null");
return null;
} else {
Log.d(TAG, "convert: returning the delegate.convert result");
return delegate.convert(body);
}
}
};
}
}
Now, in every call where there's a body, I'm getting this in the log:
```
convert: ~~~~~~~~~~~~
convert: returning the delegate.convert result
````
So, it would seem as if the Null Converter is at least trying to do its job.
However, on the call that is causing me difficulties (200 response, but empty body), I don't get those lines in the output.
All I can imagine is that the EOFException is being generated before the converter is getting a chance to protect us from the EOFException. So, even though I'm getting a 200 from the server, my code is in the error handler branch.
Yes, for pedants, I understand this is not "good", "valid" or "proper" HTML, and that the server needs to change. To that I would say that the world is not perfect. It shouldn't be so ridiculously hard to handle such a simple situation.
-Ken
In my case I'm receiving Response body "[text={"errorCode":"username_in_use","messages":null}]" but still while converting response it throwing EOFException. Any suggestion !!
converter.convert(responseBody)
hello guys i face same error in retrofit with kotlin please suggest me
use below link to see my code and give me suggestion
https://stackoverflow.com/questions/52946537
I've got a good way to handle this, just want to share with you guys :)
I create an
EmptyJsonLenientConverterFactoryto deal with empty body:public class EmptyJsonLenientConverterFactory extends Converter.Factory { private final GsonConverterFactory mGsonConverterFactory; public EmptyJsonLenientConverterFactory(GsonConverterFactory gsonConverterFactory) { mGsonConverterFactory = gsonConverterFactory; } @Override public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { return mGsonConverterFactory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, retrofit); } @Override public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { final Converter<ResponseBody, ?> delegateConverter = mGsonConverterFactory.responseBodyConverter(type, annotations, retrofit); return value -> { try { return delegateConverter.convert(value); } catch (EOFException e) { // just return null return null; } }; } }And besides, my server return api error in HTTP 200 response, so I also create a
YLApiErrorAwareConverterFactoryto catch them:public class YLApiErrorAwareConverterFactory extends Converter.Factory { private final EmptyJsonLenientConverterFactory mEmptyJsonLenientConverterFactory; public YLApiErrorAwareConverterFactory( EmptyJsonLenientConverterFactory emptyJsonLenientConverterFactory) { mEmptyJsonLenientConverterFactory = emptyJsonLenientConverterFactory; } @Override public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { return mEmptyJsonLenientConverterFactory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, retrofit); } @Override public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { final Converter<ResponseBody, ?> apiErrorConverter = mEmptyJsonLenientConverterFactory.responseBodyConverter(YLApiError.class, annotations, retrofit); final Converter<ResponseBody, ?> delegateConverter = mEmptyJsonLenientConverterFactory.responseBodyConverter(type, annotations, retrofit); return value -> { // read them all, then create a new ResponseBody for ApiError // because the response body is wrapped, we can't clone the ResponseBody correctly MediaType mediaType = value.contentType(); String stringBody = value.string(); try { Object apiError = apiErrorConverter .convert(ResponseBody.create(mediaType, stringBody)); if (apiError instanceof YLApiError && ((YLApiError) apiError).isApiError()) { throw (YLApiError) apiError; } } catch (JsonSyntaxException notApiError) { } // then create a new ResponseBody for normal body return delegateConverter.convert(ResponseBody.create(mediaType, stringBody)); }; } }And to wire them up:
Retrofit retrofit = new Retrofit.Builder().client(okHttpClient) .baseUrl(URL) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .addConverterFactory(new YLApiErrorAwareConverterFactory( new EmptyJsonLenientConverterFactory(GsonConverterFactory.create(gson)))) .build();
what's your YLApiError?
Please reopen it, it is not working. When the server has no body and code is 4xx.
A little help would be much appreciated here. Unfortunately I can't change the API I try to consume and one particular endpoint returns 202 - without any payload, no "{}", nothing - until the data is ready, then it'll return with a 200 and the proper payload.
When I to parse the 202 response it throws the exception. Any idea how to solve this? Is there a way to handle this - to handle the null response part and the valid payload too?
This is the converter:
internal val nullOnEmptyConverterFactory = object : Converter.Factory() {
fun converterFactory() = this
override fun responseBodyConverter(
type: Type,
annotations: Array<out Annotation>,
retrofit: Retrofit
) = object : Converter<ResponseBody, Any?> {
val nextResponseBodyConverter =
retrofit.nextResponseBodyConverter<Any?>(converterFactory(), type, annotations)
override fun convert(value: ResponseBody) =
if (value.contentLength() != 0L) nextResponseBodyConverter.convert(value) else null
}
}
fun provideRetrofit(factory: Gson, client: OkHttpClient): Retrofit {
return Retrofit.Builder()
.client(client)
.addConverterFactory(nullOnEmptyConverterFactory)
.addConverterFactory(ScalarsConverterFactory.create()) // used for jsonp
.addConverterFactory(GsonConverterFactory.create(factory))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.baseUrl(ApiService.getBaseUrl())
.build()
}
This is the error message I get when I'm using HttpLoggingInterceptor to monitor the calls:
2020-11-05 10:52:42.294 1878-1878 error: java.io.EOFException
at okio.RealBufferedSource.require(RealBufferedSource.kt:55)
at okio.GzipSource.consumeHeader(GzipSource.kt:104)
at okio.GzipSource.read(GzipSource.kt:62)
at okio.RealBufferedSource.request(RealBufferedSource.kt:62)
at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:253)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:112)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:87)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.kt:184)
at okhttp3.RealCall$AsyncCall.run(RealCall.kt:136)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
And this is the error message I get when I don't use the LogginInterceptor:
2020-11-05 10:59:35.246 2797-2797 error: java.io.EOFException
at okio.RealBufferedSource.require(RealBufferedSource.kt:55)
at okio.GzipSource.consumeHeader(GzipSource.kt:104)
at okio.GzipSource.read(GzipSource.kt:62)
at okio.RealBufferedSource.read(RealBufferedSource.kt:41)
at okio.ForwardingSource.read(ForwardingSource.kt:29)
at retrofit2.OkHttpCall$ExceptionCatchingResponseBody$1.read(OkHttpCall.java:288)
at okio.RealBufferedSource.select(RealBufferedSource.kt:93)
at okhttp3.internal.Util.readBomAsCharset(Util.kt:256)
at okhttp3.ResponseBody$BomAwareReader.read(ResponseBody.kt:208)
at com.google.gson.stream.JsonReader.fillBuffer(JsonReader.java:1295)
at com.google.gson.stream.JsonReader.nextNonWhitespace(JsonReader.java:1333)
at com.google.gson.stream.JsonReader.consumeNonExecutePrefix(JsonReader.java:1576)
at com.google.gson.stream.JsonReader.doPeek(JsonReader.java:534)
at com.google.gson.stream.JsonReader.peek(JsonReader.java:425)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:207)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:39)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27)
at data.network.util.NullOnEmptyConverterFactoryKt$nullOnEmptyConverterFactory$1$responseBodyConverter$1.convert(NullOnEmptyConverterFactory.kt:29)
at data.network.util.NullOnEmptyConverterFactoryKt$nullOnEmptyConverterFactory$1$responseBodyConverter$1.convert(NullOnEmptyConverterFactory.kt:18)
at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:225)
at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:121)
at okhttp3.RealCall$AsyncCall.run(RealCall.kt:138)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
Not sure if this helps, but I was able to use Response<Unit> as mentioned in here to prevent crash. https://github.com/square/retrofit/issues/3075
Thanks @andrewdittmer! After a few hours of extensive digging around on SO and in github tickets I found out that my issue is caused by the backend. The API response lacks proper header data, and that's the reason I get the exception. The thread that helped me figure it out: https://github.com/square/okhttp/issues/3646
Just a heads up, I was facing this behaviour (null body exception) even with the solutions above. Then, I updated my API to return 200 instead of 204 (no content) and it worked.
Note: As I'm using RxJava, I needed to use Observable<Unit> as well.
body.contentLength() always returns -1 on me. What to do?
I am also facing the same issue. means "Empty Body" and content length is -1, so after adding NullOnEmptyConverterFactory also getting the issue.
I think we should not add contentLength != -1 condition inside NullOnEmptyConverterFactory because https://github.com/square/okhttp/issues/3309
Is there any solution?
Most helpful comment
I had to implement this for a requirement from my services team. Here is the updated code I used for release version of Retrofit 2.0 that seems to be working in case anyone else comes across the issue.