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

  1. Wrap a real decoder: use StreamDecoder.create(defaultDecoder) where defaultDecoder handles the non-stream types (e.g., new JacksonDecoder())
  2. Change the method's return type to a Stream/Iterator type if streaming was intended
  3. 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

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


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)