OpenFeign/feign · error · DecodeException
${e.getMessage()}
Error message
${e.getMessage()} What it means
While pulling the next element from the decoded stream, the Jackson parser throws a JacksonException (malformed JSON, unexpected token, IO problem). Jackson3IteratorDecoder converts it into a Feign DecodeException carrying the HTTP status, the message, the request, and the cause. The underlying input stream is closed by the parser at that point, so iteration cannot continue.
Solutions
- Catch feign.DecodeException around iteration and inspect getCause() for the Jackson message and location (line/column)
- Verify the endpoint actually streams the expected element JSON (test with curl)
- Increase/verify proxy and read timeouts so the body is not truncated mid-stream
- Confirm Content-Type and charset match what the server sends
- Add a Deserializer/mixin if the element shape differs from the DTO
Example fix
// before
Iterator<ItemDto> it = itemsApi.stream();
while (it.hasNext()) { save(it.next()); }
// after
try (Iterator<ItemDto> it = itemsApi.stream()) {
while (it.hasNext()) {
try { save(it.next()); }
catch (DecodeException e) { log.error("stream broke at element", e); break; }
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// cheap pre-flight before iterating
if (response.status() != 200) throw new IllegalStateException("non-200: " + response.status());
String head = peekFirstBytes(response); // must look like '{' or '[' not '<html>' Try / catch
try (Iterator<ItemDto> it = decoded) {
while (it.hasNext()) {
try {
process(it.next());
} catch (DecodeException e) {
log.error("Stream decode failed (status {}): {}", e.status(), e.getCause().getMessage());
break; // parser closed the stream; iteration cannot continue
}
}
} Prevention
- Confirm with curl that the endpoint returns well-formed element JSON for the whole stream
- Set adequate read/connect timeouts so large streams are not truncated
- Verify Content-Type and charset headers match the actual payload
- Handle error pages returned with HTTP 200 before iterating
- Keep iterator usage confined (try-with-resources via the decoder's close()) so partial reads are cleaned up
When it happens
Trigger: Iterating the Iterator<T> returned from decode() when the response body contains invalid/truncated JSON, a JSON error payload instead of the expected element shape, or the stream is cut off mid-element (proxy timeout, connection reset).
Common situations: Server returns an HTML/JSON error page with status 200; NDJSON/streaming endpoints that fail midway; charset mismatches producing corrupt bytes; response body empty except whitespace where a value was expected.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- StreamDecoder supports types other than stream. When type…
- ${e.getMessage()}
- Not supported type
- Not an iterator type
- ${jsonException.getMessage()}
AI-assisted analysis of OpenFeign/feign@e2a1e27560 (2026-09-10).
Data as JSON: /api/errors/6dae42867f7f2b3f.
Report an issue: GitHub.
Appendix: source
Thrown at jackson3/src/main/java/feign/jackson3/Jackson3IteratorDecoder.java:170
try {
JsonToken jsonToken = parser.nextToken();
if (jsonToken == null) {
return null;
}
if (jsonToken == JsonToken.START_ARRAY) {
jsonToken = parser.nextToken();
}
if (jsonToken == JsonToken.END_ARRAY) {
ensureClosed(this);
return null;
}
return objectReader.readValue(parser);
} catch (JacksonException e) {
// Input Stream closed automatically by parser
throw new DecodeException(response.status(), e.getMessage(), response.request(), e);
}
}
@Override
public T next() {
if (current != null) {
T tmp = current;
current = null;
return tmp;
}
T next = readNext();
if (next == null) {
throw new NoSuchElementException();
}
return next;
}
@OverrideView on GitHub (pinned to e2a1e27560)