{"id":"91da2cc15c80a218","repo":"square/retrofit","slug":"result-must-be-parameterized-as-result-foo-or-res-91da2c","errorCode":null,"errorMessage":"Result must be parameterized as Result<Foo> or Result<? extends Foo>","messagePattern":"Result must be parameterized as Result<Foo> or Result<\\? extends Foo>","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"retrofit-adapters/rxjava3/src/main/java/retrofit2/adapter/rxjava3/RxJava3CallAdapterFactory.java","lineNumber":143,"sourceCode":"              + \" return type must be parameterized\"\n              + \" as \"\n              + name\n              + \"<Foo> or \"\n              + name\n              + \"<? extends Foo>\");\n    }\n\n    Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);\n    Class<?> rawObservableType = getRawType(observableType);\n    if (rawObservableType == Response.class) {\n      if (!(observableType instanceof ParameterizedType)) {\n        throw new IllegalStateException(\n            \"Response must be parameterized\" + \" as Response<Foo> or Response<? extends Foo>\");\n      }\n      responseType = getParameterUpperBound(0, (ParameterizedType) observableType);\n    } else if (rawObservableType == Result.class) {\n      if (!(observableType instanceof ParameterizedType)) {\n        throw new IllegalStateException(\n            \"Result must be parameterized\" + \" as Result<Foo> or Result<? extends Foo>\");\n      }\n      responseType = getParameterUpperBound(0, (ParameterizedType) observableType);\n      isResult = true;\n    } else {\n      responseType = observableType;\n      isBody = true;\n    }\n\n    return new RxJava3CallAdapter(\n        responseType, scheduler, isAsync, isResult, isBody, isFlowable, isSingle, isMaybe, false);\n  }\n}\n","sourceCodeStart":125,"sourceCodeEnd":157,"githubUrl":"https://github.com/square/retrofit/blob/d0b112dad073b7fe49c953ebc46ff1b424cb1e51/retrofit-adapters/rxjava3/src/main/java/retrofit2/adapter/rxjava3/RxJava3CallAdapterFactory.java#L125-L157","documentation":"Thrown by the RxJava3 call adapter factory when a service method returns an RxJava type parameterized with the raw retrofit2.adapter.rxjava3.Result class (no type argument). Result is Retrofit's wrapper that emits both Response and errors in the stream, but the factory needs the inner body type (line 146) to construct the adapter, so a raw Result is rejected at method-resolution time as an IllegalStateException.","triggerScenarios":"A method declared as `@GET(\"x\") Observable<Result> get();` (raw Result). The check at RxJava3CallAdapterFactory.java:141-145 fires when rawObservableType == Result.class but observableType is not a ParameterizedType.","commonSituations":"Developer switches from `Observable<User>` to the Result wrapper to capture HttpException/network errors in onNext but forgets the inner type; mixing up retrofit2.Response and the RxJava3 Result wrapper; copy-paste error; Kotlin generic elision.","solutions":["Parameterize Result with the body type: `Observable<Result<User>>`.","If you do not need error-as-value semantics, use `Observable<User>` or `Observable<Response<User>>` instead.","Remember Result only emits one item (Response or error) and never both, so the inner type is mandatory."],"exampleFix":"// before\n@GET(\"users/{id}\")\nFlowable<Result> getUser(@Path(\"id\") String id);\n\n// after\n@GET(\"users/{id}\")\nFlowable<Result<User>> getUser(@Path(\"id\") String id);","handlingStrategy":"validation","validationCode":"// Startup smoke test asserting Result is never used raw.\n@Test void resultReturnTypesAreParameterized() throws Exception {\n  Retrofit retrofit = new Retrofit.Builder()\n      .baseUrl(\"http://localhost/\")\n      .addCallAdapterFactory(RxJava3CallAdapterFactory.create())\n      .build();\n  retrofit.create(MyService.class);\n  for (java.lang.reflect.Method m : MyService.class.getDeclaredMethods()) {\n    if (m.isDefault()) continue;\n    Type t = m.getGenericReturnType();\n    if (t instanceof java.lang.reflect.ParameterizedType) {\n      Type inner = ((java.lang.reflect.ParameterizedType) t).getActualTypeArguments()[0];\n      assertNotEquals(retrofit2.adapter.rxjava3.Result.class, inner,\n          m.getName() + \" uses raw Result; use Result<BodyType>\");\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  MyService service = retrofit.create(MyService.class);\n  // exercise each method in a startup smoke test to trigger resolution\n} catch (IllegalStateException e) {\n  throw new IllegalStateException(\n      \"Retrofit service method return type is invalid: \" + e.getMessage(), e);\n}","preventionTips":["Always parameterize Result: Observable<Result<User>>.","Remember Result<T> emits either a Response<T> or an error, never both, so the inner type is required.","Do not confuse retrofit2.Response with the RxJava3 Result wrapper.","Add a build-time test that resolves every service method return type."],"tags":["rxjava3","retrofit","generics","java","call-adapter"],"analyzedSha":"d0b112dad073b7fe49c953ebc46ff1b424cb1e51","analyzedAt":"2026-08-04T19:12:59.096Z","schemaVersion":2}