25

I put the following interceptor on my OkHttp client:

httpClient.addInterceptor(new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Response response = chain.proceed(chain.request());
        Log.d("Response", response.body().string());
        return response;
    }
    });

However, this isn't playing nice with Retrofit 2. It seems that you can only read the stream from the response once and that might be what is causing the exception. I'm thinking retrofit is trying to parse the stream which the log already parsed. How then do I get my hands on the response? I'm currently trying to debug a very nasty and strange malformed json exception.

This is the exception stack trace:

07 - 28 10: 58: 21.575 22401 - 22529 / REDACTED E / AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
    Process: REDACTED, PID: 22401
    java.lang.IllegalStateException: closed
    at okhttp3.internal.http.Http1xStream$FixedLengthSource.read(Http1xStream.java: 378)
    at okio.Buffer.writeAll(Buffer.java: 956)
    at okio.RealBufferedSource.readByteArray(RealBufferedSource.java: 92)
    at okhttp3.ResponseBody.bytes(ResponseBody.java: 83)
    at okhttp3.ResponseBody.string(ResponseBody.java: 109)
    at REDACTED.ServiceGenerator$2.intercept(ServiceGenerator.java: 90)
    at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java: 187)
    at REDACTED.ServiceGenerator$2.intercept(ServiceGenerator.java: 89)
    at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java: 187)
    at REDACTED.ServiceGenerator$2.intercept(ServiceGenerator.java: 89)
    at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java: 187)
    at REDACTED.ServiceGenerator$2.intercept(ServiceGenerator.java: 89)
    at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java: 187)
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java: 160)
    at okhttp3.RealCall.access$100(RealCall.java: 30)
    at okhttp3.RealCall$AsyncCall.execute(RealCall.java: 127)
    at okhttp3.internal.NamedRunnable.run(NamedRunnable.java: 32)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java: 1112)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java: 587)
    at java.lang.Thread.run(Thread.java: 841)

I see that there are multiple interceptors in the stack but I only ever explicitly add the one, which is the on that is throwing the exception.

Mr.7
  • 2,292
  • 1
  • 20
  • 33

6 Answers6

45

You are consuming the response body in the interceptor, so you're going to want to create a new response:

@Override public Response intercept(Chain chain) throws IOException {
  Response response = chain.proceed(chain.request());
  ResponseBody body = response.body();
  String bodyString = body.string();
  MediaType contentType = body.contentType();
  Log.d("Response", bodyString);
  return response.newBuilder().body(ResponseBody.create(contentType, bodyString)).build();
}

You may also want to check out the logging interceptor in OkHttp's repo: https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor

Eric Cochran
  • 8,414
  • 5
  • 50
  • 91
  • @Eric I did exactly as you did and keep getting the same error. I did comment the bodyString line (so the body.string() wont be called) and put a random String as the body. Any ideais? – Felipe Castilhos May 23 '17 at 17:17
  • if (response.code() == HTTP_OK) { Context context = AutoAtendimentoApplication.getContext(); String request = response.request().url().toString(); ResponseBody body = response.body(); String bodyString = body.string(); okhttp3.MediaType contentType = body.contentType(); CacheHelper.setCache(context, request, bodyString); return response.newBuilder().body(ResponseBody.create(contentType, bodyString)).build(); } – Felipe Castilhos May 23 '17 at 17:17
  • @FelipeCastilhos are you getting `java.lang.IllegalStateException: closed`? there could be a consumer of your response at another point. probably check your detailed stack trace. – Eric Cochran May 23 '17 at 20:21
  • I did a clean and rebuild and everything work it out. Thanks for the reply! – Felipe Castilhos May 24 '17 at 22:56
36

Do not call response body more than once, since it is read as a stream and not stored in memory.

Maybe you are calling response.body().string() more than once because response body can be huge so OkHttp doesn’t store it in memory, it reads it as a stream from network when you need it.

When you read body as a string() OkHttp downloads response body and returns it to you without keeping reference to the string, it can’t be downloaded twice without new request.

https://github.com/square/okhttp/issues/1240

Community
  • 1
  • 1
9

I also got this exception due to trying to convert response body 2X. First i logged the response.body().string() & then again i passed the same to a method. Which was causing the exception. Check if the response body is converted to string more than once.

Gaurav Pandit
  • 939
  • 1
  • 8
  • 13
  • (This post does not seem to provide a [quality answer](https://stackoverflow.com/help/how-to-answer) to the question. Please either edit your answer and improve it, or just post it as a comment to the question). – sɐunıɔןɐqɐp Sep 11 '18 at 09:12
1

Response body can be huge so OkHttp doesn’t store it in memory, it reads it as a stream from network when you need it.

When you read body as a string() OkHttp downloads response body and returns it to you without keeping reference to the string, it can’t be downloaded twice without new request.

0

The error is weird but the the first problem I see is that you are using OKHTTP3 and trying to add a builder to the already created client.

The OkHttpClient is immutable now and you will have to add the interceptors directly to the OkHttpClient.Builder by calling addInterceptor method on the builder.

For more clarity on how to do this see this Github Issue. I think this should solve your problem.

Aashrai Ravooru
  • 131
  • 2
  • 6
  • Thanks for your response. The variable you're seeing is actually the builder but is poorly named. Also, I figured out my problem. You can look in this class to see how to intercept a response and not effect its state (involves getting the connection itself): https://github.com/square/okhttp/blob/master/okhttp-logging-interceptor/src/main/java/okhttp3/logging/HttpLoggingInterceptor.java – Elliott Gregory Dehnbostel Jul 28 '16 at 18:27
  • @ElliottGregoryDehnbostel that doesn't exist anymore it seems. – Fran Marzoa Feb 01 '23 at 17:12
0

I have this error when try to upload file (image) and use Chrome DevTools (Stetho).

Turning off Chrome DevTools solve my problem