OpenFeign/feign · error · IllegalStateException

Expected only one contained type.

Error message

Expected only one contained type.

What it means

After confirming the method returns a Publisher, the contract reads the Publisher's actual type arguments to rewrite the method's return type to the contained type. If the Publisher declares more than one type parameter, the contained type is ambiguous, so an IllegalStateException is thrown. Standard reactive types only have one type parameter, so this is an internal invariant check.

Solutions

  1. Use standard Publisher types (Flux<T>, Mono<T>, Observable<T>) which have exactly one type parameter
  2. Remove extra type parameters from any custom Publisher subtype used as a return type
  3. If you need multiple values, wrap them in a single POJO and return Publisher<YourPojo>

Example fix

// before
class WeirdPublisher<T, U> implements Publisher<T> {}
WeirdPublisher<String, Long> get();
// after
Flux<String> get(); // one type parameter only
Defensive patterns

Strategy: type-guard

Validate before calling

if (Publisher.class.isAssignableFrom(raw) && ((ParameterizedType) type).getActualTypeArguments().length != 1)
  throw new IllegalStateException("Publisher return types must have exactly one type parameter");

Type guard

static boolean hasSingleTypeParameter(Type t) {
  return t instanceof ParameterizedType
      && ((ParameterizedType) t).getActualTypeArguments().length == 1;
}

Prevention

When it happens

Trigger: Declaring a return type parameterized with more than one type argument that (transitively) implements Publisher, e.g. a custom interface extending Publisher with an extra type parameter, which passes isReactive() but yields actualTypes.length > 1.

Common situations: Custom reactive wrapper classes or interfaces with extra generics; third-party reactive types parameterized unconventionally; raw misuse of ParameterizedType contracts.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of OpenFeign/feign@e2a1e27560 (2026-09-10). Data as JSON: /api/errors/0c9eeed697671b4d. Report an issue: GitHub.

Appendix: source

Thrown at reactive/src/main/java/feign/reactive/ReactiveDelegatingContract.java:54

  public List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType) {
    List<MethodMetadata> methodsMetadata = this.delegate.parseAndValidateMetadata(targetType);

    for (final MethodMetadata metadata : methodsMetadata) {
      final Type type = metadata.returnType();
      if (!isReactive(type)) {
        throw new IllegalArgumentException(
            String.format(
                "Method %s of contract %s doesn't returns a org.reactivestreams.Publisher",
                metadata.configKey(), targetType.getSimpleName()));
      }

      /*
       * we will need to change the return type of the method to match the return type contained
       * within the Publisher
       */
      Type[] actualTypes = ((ParameterizedType) type).getActualTypeArguments();
      if (actualTypes.length > 1) {
        throw new IllegalStateException("Expected only one contained type.");
      } else {
        Class<?> actual = Types.getRawType(actualTypes[0]);
        if (Stream.class.isAssignableFrom(actual)) {
          throw new IllegalArgumentException(
              "Streams are not supported when using Reactive Wrappers");
        }
        metadata.returnType(type);
      }
    }

    return methodsMetadata;
  }

  /**
   * Ensure that the type provided implements a Reactive Streams Publisher.
   *
   * @param type to inspect.
   * @return true if the type implements the Reactive Streams Publisher specification.

View on GitHub (pinned to e2a1e27560)