OpenFeign/feign · error · IllegalArgumentException
StreamDecoder supports types other than stream. When type…
Error message
StreamDecoder supports types other than stream. When type is not stream, the delegate decoder needs to be setting.
What it means
StreamDecoder decodes streaming (Iterator/stream) response types itself, but for any other return type it must delegate to another Decoder. If no delegate decoder was configured and the method's return type is not a Stream type, it throws this IllegalArgumentException at decode time.
Solutions
- Wrap a real decoder: use StreamDecoder.create(defaultDecoder) where defaultDecoder handles the non-stream types (e.g., new JacksonDecoder())
- Change the method's return type to a Stream/Iterator type if streaming was intended
- Catch IllegalArgumentException around the call as a last resort to detect misconfiguration early
Example fix
// before Decoder decoder = StreamDecoder.create(); // no delegate // after Decoder decoder = StreamDecoder.create(new JacksonDecoder(mapper)); // handles non-stream types
Defensive patterns
Strategy: validation
Validate before calling
if (client has any non-Stream return types && decoder is StreamDecoder) {
throw new IllegalStateException("StreamDecoder requires a delegate decoder for non-stream types");
} Try / catch
try {
return api.getItem(id);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("delegate decoder")) {
throw new IllegalStateException("configure StreamDecoder.create(delegate)", e);
}
throw e;
} Prevention
- Always pass a delegate: StreamDecoder.create(new JacksonDecoder())
- Audit client interfaces for mixed Stream and non-Stream return types before configuring only a StreamDecoder
- Add a startup test that invokes one method per return-type shape
When it happens
Trigger: Building a Feign client with StreamDecoder.create() (no delegate) or StreamDecoder.create(null) while an interface method returns a non-stream type (e.g., a plain POJO, List, String); the error fires when that method is invoked and the response is decoded.
Common situations: Mixing streaming endpoints and normal endpoints in one client but configuring only StreamDecoder without a wrapped fallback decoder; copy-pasting the streaming setup from docs onto a client with regular return types.
Understand the failure class
Background: "X is required", "must be set", "cannot be empty": the missing-required-config error family, from Vertex AI project/location to WeChat keys — this error's family across 18 libraries.
Related errors
- ${e.getMessage()}
- Status Code [ ] has already been declared to throw [ ] and…
- Cannot generate exception - check constructor parameter…
- Too many constructors marked with @FeignExceptionConstructor
- Cannot find any suitable constructor in class
AI-assisted analysis of OpenFeign/feign@e2a1e27560 (2026-09-10).
Data as JSON: /api/errors/003a0f4fcda13e82.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/feign/stream/StreamDecoder.java:71
* Stream<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
* }</code>
* </pre>
*/
public final class StreamDecoder implements Decoder {
private final Decoder iteratorDecoder;
private final Optional<Decoder> delegateDecoder;
StreamDecoder(Decoder iteratorDecoder, Decoder delegateDecoder) {
this.iteratorDecoder = iteratorDecoder;
this.delegateDecoder = Optional.ofNullable(delegateDecoder);
}
@Override
public Object decode(Response response, Type type) throws IOException, FeignException {
if (!isStream(type)) {
if (!delegateDecoder.isPresent()) {
throw new IllegalArgumentException(
"StreamDecoder supports types other than stream. "
+ "When type is not stream, the delegate decoder needs to be setting.");
} else {
return delegateDecoder.get().decode(response, type);
}
}
ParameterizedType streamType = (ParameterizedType) type;
Iterator<?> iterator =
(Iterator<?>) iteratorDecoder.decode(response, new IteratorParameterizedType(streamType));
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false)
.onClose(
() -> {
if (iterator instanceof Closeable) {
ensureClosed((Closeable) iterator);
} else {
ensureClosed(response);
}View on GitHub (pinned to e2a1e27560)