{"id":"18ee2568241a0e5c","repo":"square/retrofit","slug":"response-must-be-parameterized-as-response-foo-or-18ee25","errorCode":null,"errorMessage":"Response must be parameterized as Response<Foo> or Response<? extends Foo>","messagePattern":"Response must be parameterized as Response<Foo> or Response<\\? extends Foo>","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"retrofit-adapters/rxjava3/src/main/java/retrofit2/adapter/rxjava3/RxJava3CallAdapterFactory.java","lineNumber":137,"sourceCode":"    Type responseType;\n    if (!(returnType instanceof ParameterizedType)) {\n      String name =\n          isFlowable ? \"Flowable\" : isSingle ? \"Single\" : isMaybe ? \"Maybe\" : \"Observable\";\n      throw new IllegalStateException(\n          name\n              + \" 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  }","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/square/retrofit/blob/d0b112dad073b7fe49c953ebc46ff1b424cb1e51/retrofit-adapters/rxjava3/src/main/java/retrofit2/adapter/rxjava3/RxJava3CallAdapterFactory.java#L119-L155","documentation":"Thrown by the RxJava3 call adapter factory while Retrofit resolves a service method's return type: the outer RxJava type (Observable/Flowable/Single/Maybe) is parameterized with the raw retrofit2.Response class instead of Response<T>. Because the factory must extract the body type to build a CallAdapter (see getParameterUpperBound at line 140), a raw Response has no body type to extract, so it fails fast. It surfaces as IllegalStateException the first time the offending method is invoked through the Retrofit proxy.","triggerScenarios":"A service method declared as `@GET(\"users/{id}\") Observable<Response> getUser(@Path(\"id\") String id);` (raw Response, no type argument). The check at RxJava3CallAdapterFactory.java:135-139 fires when rawObservableType == Response.class but observableType is not a ParameterizedType.","commonSituations":"Developer wraps a working `Observable<User>` in Response to gain access to headers/status codes but forgets the inner type argument; IDE auto-imports the raw retrofit2.Response; copy-paste from samples that elided generics; Kotlin platform types where the generic was omitted.","solutions":["Parameterize Response with the body type: `Observable<Response<User>> getUser(...)`.","If you only need the body, drop the wrapper entirely: `Observable<User>`.","If you want both success and error emitted into the stream, use `Observable<Result<User>>` instead."],"exampleFix":"// before\n@GET(\"users/{id}\")\nObservable<Response> getUser(@Path(\"id\") String id);\n\n// after\n@GET(\"users/{id}\")\nObservable<Response<User>> getUser(@Path(\"id\") String id);","handlingStrategy":"validation","validationCode":"// Startup smoke test: force resolution of each service method's return type\n// so raw-Response misuse fails at build/init time, not at the first user request.\n@Test void serviceReturnTypesAreParameterized() throws Exception {\n  Retrofit retrofit = new Retrofit.Builder()\n      .baseUrl(\"http://localhost/\")\n      .addCallAdapterFactory(RxJava3CallAdapterFactory.create())\n      .build();\n  MyService svc = 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.Response.class, inner,\n          m.getName() + \" uses raw Response; use Response<BodyType>\");\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":"// Wrap retrofit.create() / first invocation at app init to fail fast with a\n// clear, attributable message. (Retrofit resolves return types lazily on\n// first proxy invocation, so force it during startup validation.)\ntry {\n  MyService service = retrofit.create(MyService.class);\n  // touch each method's return-type resolution via your smoke test\n} catch (IllegalStateException e) {\n  throw new IllegalStateException(\n      \"Retrofit service method has an invalid return type: \" + e.getMessage(), e);\n}","preventionTips":["Always specify the inner type when wrapping with Response, e.g. Observable<Response<User>>.","Keep a MockWebServer integration test that exercises every service method at build time.","Treat IDE raw-type warnings as errors.","In Kotlin, annotate platform-typed returns explicitly to avoid generic elision."],"tags":["rxjava3","retrofit","generics","java","call-adapter"],"analyzedSha":"d0b112dad073b7fe49c953ebc46ff1b424cb1e51","analyzedAt":"2026-08-04T19:12:59.096Z","schemaVersion":2}