lysine-dev/retrofit · error · JsonIOException
JSON document was not fully consumed.
Error message
JSON document was not fully consumed.
What it means
Thrown by GsonResponseBodyConverter.convert() as a JsonIOException after the registered TypeAdapter finishes reading. The converter reads one JSON value from the response body, then peeks the JsonReader; if the next token is not END_DOCUMENT (i.e. there is trailing content after the top-level value), it refuses to return a partial/ambiguous result and throws. This guards against silently ignoring malformed or concatenated JSON payloads.
Source
Thrown at retrofit-converters/gson/src/main/java/retrofit2/converter/gson/GsonResponseBodyConverter.java:42
import okhttp3.ResponseBody;
import retrofit2.Converter;
final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final Gson gson;
private final TypeAdapter<T> adapter;
GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override
public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
T result = adapter.read(jsonReader);
if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
throw new JsonIOException("JSON document was not fully consumed.");
}
return result;
} finally {
value.close();
}
}
}
View on GitHub (pinned to d0b112dad0)
Solutions
- Inspect the raw response body (e.g. via an OkHttp logging interceptor at BODY level) to identify the trailing content and fix the server/proxy emitting it.
- If concatenated documents are intentional, switch to a streaming reader that loops on peek()!=END_DOCUMENT instead of using GsonResponseBodyConverter.
- If a custom TypeAdapter is leaving the reader mid-value, fix the adapter to fully consume its token range.
- Disable any server debug/echo flags or proxy body-rewriting that appends content after the JSON payload.
Example fix
// before — server returns: {"id":1}{"id":2}
// converter throws after reading the first object.
// after — fix the endpoint to return a single JSON value or array:
[{"id":1},{"id":2}] Defensive patterns
Strategy: try-catch
Validate before calling
// Validate the body shape before handing it to Retrofit/Gson when the source is suspect.
// This is only for non-Retrofit call paths; Retrofit owns ResponseBody reading internally.
String body = response.body().string();
JsonReader reader = new JsonReader(new StringReader(body));
new Gson().getAdapter(SomeType.class).read(reader);
if (reader.peek() != JsonToken.END_DOCUMENT) {
log.warn("Trailing JSON content detected; investigate the server response.");
} Try / catch
// Wrap the converter call (typically inside a custom Converter or interceptor) and surface a
// parse error with context rather than letting JsonIOException escape raw.
try {
return converter.convert(response.body());
} catch (JsonIOException e) {
if ("JSON document was not fully consumed.".equals(e.getMessage())) {
throw new IllegalStateException("Server returned trailing JSON content; see raw body logs", e);
}
throw e;
} Prevention
- Enable an OkHttp logging interceptor at BODY level in non-prod builds to see raw responses.
- Investigate any server endpoint that returns concatenated JSON objects or appended diagnostics.
- Ensure custom TypeAdapters consume their full token range, not a prefix.
When it happens
Trigger: An HTTP response body that contains extra data after the single expected JSON value: multiple concatenated JSON objects, a JSON value followed by trailing characters/whitespace-with-content, a server streaming multiple documents into one body, or a proxy that appended diagnostics after the JSON.
Common situations: A backend that emits `{...}{...}` (two objects) in a single response, a misconfigured CDN/edge proxy appending analytics or error HTML, a debug-mode server echoing the request body after the response JSON, or a partial read by a custom TypeAdapter that consumed only part of the value.
Related errors
- gson == null
- ListenableFuture return type must be parameterized as Listen
- Response must be parameterized as Response<Foo> or Response<
- CompletableFuture return type must be parameterized as Compl
- Response must be parameterized as Response<Foo> or Response<
AI-assisted analysis of lysine-dev/retrofit@d0b112dad0 (2026-08-13).
Data as JSON: /api/errors/c6a54bcdfccdccf3.
Report an issue: GitHub.