{"record":{"id":"fec9c904d9f92380","repo":"OpenFeign/feign","slug":"method-s-of-contract-s-doesn-t-returns-a-org-rea","errorCode":null,"errorMessage":"Method %s of contract %s doesn't returns a org.reactivestreams.Publisher","messagePattern":"Method (.+?) of contract (.+?) doesn't returns a org\\.reactivestreams\\.Publisher","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"reactive/src/main/java/feign/reactive/ReactiveDelegatingContract.java","lineNumber":42,"sourceCode":"import java.util.stream.Stream;\nimport org.reactivestreams.Publisher;\n\npublic class ReactiveDelegatingContract implements Contract {\n\n  private final Contract delegate;\n\n  ReactiveDelegatingContract(Contract delegate) {\n    this.delegate = delegate;\n  }\n\n  @Override\n  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        }","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/OpenFeign/feign/blob/e2a1e27560a1e68840c34f031afca88b36096e30/reactive/src/main/java/feign/reactive/ReactiveDelegatingContract.java#L24-L60","documentation":"ReactiveDelegatingContract validates that every method of the target interface returns a type implementing org.reactivestreams.Publisher (e.g. Flux, Mono, Observable). If any method's declared return type is not reactive, the contract cannot wrap it, so parseAndValidateMetadata throws IllegalArgumentException. This is a fail-fast check during Feign target creation.","triggerScenarios":"Declaring a Feign interface method with a plain return type (String, Response, List<T>, CompletableFuture, etc.) and passing the interface to ReactorFeign/RxJavaFeign via a ReactiveDelegatingContract-based builder.","commonSituations":"Reusing an existing synchronous Feign interface with a reactive builder; migrating code from classic Feign.builder() to the reactive module without changing return types; copy-pasting method signatures from non-reactive clients.","solutions":["Change every method in the target interface to return a Publisher subtype (reactor Flux/Mono or rx.Observable depending on module)","If some methods must stay synchronous, split them into a separate interface and build a second, non-reactive Feign target","Verify you are using the correct builder: reactor module expects Flux/Mono, rxjava module expects Observable/Single"],"exampleFix":"// before\ninterface GitHub {\n  @RequestLine(\"GET /repos/{owner}/{repo}\")\n  String repo(String owner, String repo);\n}\n// after\ninterface GitHub {\n  @RequestLine(\"GET /repos/{owner}/{repo}\")\n  Mono<String> repo(String owner, String repo); // or Flux<T> for collections\n}","handlingStrategy":"validation","validationCode":"for (java.lang.reflect.Method m : Api.class.getMethods()) {\n  if (!org.reactivestreams.Publisher.class.isAssignableFrom(m.getReturnType()))\n    throw new IllegalStateException(m + \" must return a Publisher (Flux/Mono)\");\n}","typeGuard":"static boolean isReactiveReturn(java.lang.reflect.Method m) {\n  return org.reactivestreams.Publisher.class.isAssignableFrom(m.getReturnType());\n}","tryCatchPattern":null,"preventionTips":["Define every method of reactive Feign interfaces with Flux/Mono/Observable return types from the start","Keep synchronous interfaces separate from reactive ones","Write a unit test that builds the reactive target early (fails fast at startup)"],"tags":["reactive","contract-validation","return-type"],"backgroundTag":"invalid-argument-value","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"}