{"record":{"id":"0c9eeed697671b4d","repo":"OpenFeign/feign","slug":"expected-only-one-contained-type","errorCode":null,"errorMessage":"Expected only one contained type.","messagePattern":"Expected only one contained type\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"reactive/src/main/java/feign/reactive/ReactiveDelegatingContract.java","lineNumber":54,"sourceCode":"  public List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType) {\n    List<MethodMetadata> methodsMetadata = this.delegate.parseAndValidateMetadata(targetType);\n\n    for (final MethodMetadata metadata : methodsMetadata) {\n      final Type type = metadata.returnType();\n      if (!isReactive(type)) {\n        throw new IllegalArgumentException(\n            String.format(\n                \"Method %s of contract %s doesn't returns a org.reactivestreams.Publisher\",\n                metadata.configKey(), targetType.getSimpleName()));\n      }\n\n      /*\n       * we will need to change the return type of the method to match the return type contained\n       * within the Publisher\n       */\n      Type[] actualTypes = ((ParameterizedType) type).getActualTypeArguments();\n      if (actualTypes.length > 1) {\n        throw new IllegalStateException(\"Expected only one contained type.\");\n      } else {\n        Class<?> actual = Types.getRawType(actualTypes[0]);\n        if (Stream.class.isAssignableFrom(actual)) {\n          throw new IllegalArgumentException(\n              \"Streams are not supported when using Reactive Wrappers\");\n        }\n        metadata.returnType(type);\n      }\n    }\n\n    return methodsMetadata;\n  }\n\n  /**\n   * Ensure that the type provided implements a Reactive Streams Publisher.\n   *\n   * @param type to inspect.\n   * @return true if the type implements the Reactive Streams Publisher specification.","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/OpenFeign/feign/blob/e2a1e27560a1e68840c34f031afca88b36096e30/reactive/src/main/java/feign/reactive/ReactiveDelegatingContract.java#L36-L72","documentation":"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.","triggerScenarios":"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.","commonSituations":"Custom reactive wrapper classes or interfaces with extra generics; third-party reactive types parameterized unconventionally; raw misuse of ParameterizedType contracts.","solutions":["Use standard Publisher types (Flux<T>, Mono<T>, Observable<T>) which have exactly one type parameter","Remove extra type parameters from any custom Publisher subtype used as a return type","If you need multiple values, wrap them in a single POJO and return Publisher<YourPojo>"],"exampleFix":"// before\nclass WeirdPublisher<T, U> implements Publisher<T> {}\nWeirdPublisher<String, Long> get();\n// after\nFlux<String> get(); // one type parameter only\n","handlingStrategy":"type-guard","validationCode":"if (Publisher.class.isAssignableFrom(raw) && ((ParameterizedType) type).getActualTypeArguments().length != 1)\n  throw new IllegalStateException(\"Publisher return types must have exactly one type parameter\");","typeGuard":"static boolean hasSingleTypeParameter(Type t) {\n  return t instanceof ParameterizedType\n      && ((ParameterizedType) t).getActualTypeArguments().length == 1;\n}","tryCatchPattern":null,"preventionTips":["Stick to Flux/Mono/Observable as return types; avoid custom Publisher subtypes with extra generics"],"tags":["reactive","generics","contract-validation"],"backgroundTag":"internal-invariant-violation","analyzedSha":"e2a1e27560a1e68840c34f031afca88b36096e30","analyzedAt":"2026-09-10T12:37:37.238Z","contentChangedAt":"2026-09-10T12:37:37.238Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}