OpenFeign/feign · error · IllegalArgumentException
Not an iterator type
Error message
Not an iterator type ${rawType} What it means
Jackson3IteratorDecoder requires the raw type of the declared return type to be exactly java.util.Iterator. Any other parameterized type (List<T>, Page<T>, etc.) throws IllegalArgumentException('Not an iterator type ...').
Solutions
- Change the return type to Iterator<ItemDto> if streaming is desired
- Keep List<ItemDto> and use Jackson3Decoder instead of the iterator decoder
- Register Jackson3IteratorDecoder only for specific methods/targets that genuinely stream Iterator<T>
- If wrapping in a custom container, implement a dedicated Decoder rather than reusing this one
Example fix
// before
@Get("/items") List<ItemDto> getItems(); // with Jackson3IteratorDecoder
// after
@Get("/items") Iterator<ItemDto> getItems(); // with Jackson3IteratorDecoder Defensive patterns
Strategy: type-guard
Validate before calling
Type t = method.getGenericReturnType();
if (t instanceof ParameterizedType
&& ((ParameterizedType) t).getRawType() != java.util.Iterator.class) {
throw new IllegalStateException("Jackson3IteratorDecoder needs Iterator<T>, got: " + t);
} Type guard
static boolean isExactlyIterator(Type t) {
return t instanceof ParameterizedType
&& java.util.Iterator.class.equals(((ParameterizedType) t).getRawType());
} Try / catch
try {
return decoder.decode(response, type);
} catch (IllegalArgumentException e) {
if (e.getMessage().startsWith("Not an iterator type")) {
return fallbackListDecoder.decode(response, type);
}
throw e;
} Prevention
- Keep Iterator<T> and List<T> endpoints on separate decoders - do not register the iterator decoder globally
- Document per-interface which decoder is expected
- Add startup-time validation of return types when configuring the Feign builder
- Prefer Iterator<ItemDto> for large/streaming responses; List<ItemDto> otherwise
When it happens
Trigger: Declaring a Feign method returning List<ItemDto>, Set<ItemDto>, or any other generic type and decoding with Jackson3IteratorDecoder; the guard Iterator.class.equals(parameterizedType.getRawType()) fails at decode time.
Common situations: Registering the iterator decoder as the global decoder while most endpoints return List<T>; switching a method's return type from Iterator<T> to List<T> without changing the decoder configuration.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Not supported type
- Not supported type
- Not an iterator type
- ${e.getMessage()}
- JAXB only supports decoding raw types. Found
AI-assisted analysis of OpenFeign/feign@e2a1e27560 (2026-09-10).
Data as JSON: /api/errors/85ac56fb64861d6e.
Report an issue: GitHub.
Appendix: source
Thrown at jackson3/src/main/java/feign/jackson3/Jackson3IteratorDecoder.java:104
}
reader.reset();
return new Jackson3Iterator<Object>(
actualIteratorTypeArgument(type), mapper, response, reader);
} catch (JacksonException e) {
if (e.getCause() != null && e.getCause() instanceof IOException) {
throw IOException.class.cast(e.getCause());
}
throw e;
}
}
private static Type actualIteratorTypeArgument(Type type) {
if (!(type instanceof ParameterizedType)) {
throw new IllegalArgumentException("Not supported type " + type.toString());
}
ParameterizedType parameterizedType = (ParameterizedType) type;
if (!Iterator.class.equals(parameterizedType.getRawType())) {
throw new IllegalArgumentException(
"Not an iterator type " + parameterizedType.getRawType().toString());
}
return ((ParameterizedType) type).getActualTypeArguments()[0];
}
public static Jackson3IteratorDecoder create() {
return create(Collections.<JacksonModule>emptyList());
}
public static Jackson3IteratorDecoder create(Iterable<JacksonModule> modules) {
return new Jackson3IteratorDecoder(
JsonMapper.builder()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
// Disable FAIL_ON_TRAILING_TOKENS for iterator: we read a JSON array element by
// element, so there are always "trailing tokens" (the remaining array elements)
.disable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)
.addModules(modules)
.build());View on GitHub (pinned to e2a1e27560)