{"id":"a4848f3a62ed5b45","repo":"square/retrofit","slug":"name-return-type-must-be-parameterized-as-nam-a4848f","errorCode":null,"errorMessage":"${name} return type must be parameterized as ${name}<Foo> or ${name}<? extends Foo>","messagePattern":"(.+?) return type must be parameterized as (.+?)<Foo> or (.+?)<\\? extends Foo>","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"retrofit-adapters/rxjava2/src/main/java/retrofit2/adapter/rxjava2/RxJava2CallAdapterFactory.java","lineNumber":118,"sourceCode":"      // can only be created with a single configuration.\n      return new RxJava2CallAdapter(\n          Void.class, scheduler, isAsync, false, true, false, false, false, true);\n    }\n\n    boolean isFlowable = rawType == Flowable.class;\n    boolean isSingle = rawType == Single.class;\n    boolean isMaybe = rawType == Maybe.class;\n    if (rawType != Observable.class && !isFlowable && !isSingle && !isMaybe) {\n      return null;\n    }\n\n    boolean isResult = false;\n    boolean isBody = false;\n    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) {","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/square/retrofit/blob/d0b112dad073b7fe49c953ebc46ff1b424cb1e51/retrofit-adapters/rxjava2/src/main/java/retrofit2/adapter/rxjava2/RxJava2CallAdapterFactory.java#L100-L136","documentation":"Thrown by RxJava2CallAdapterFactory while validating a service method's return type. `${name}` resolves to one of `Flowable`, `Single`, `Maybe`, or `Observable` (chosen at RxJava2CallAdapterFactory.java:117). A raw reactive type gives no body type, so no converter can be selected; the check at line 115 (`returnType instanceof ParameterizedType`) rejects it. Completable is handled separately and does not require a type parameter.","triggerScenarios":"A service method returns a raw `Observable`, `Flowable`, `Single`, or `Maybe`, e.g. `@GET(\"/\") Observable getUser();`. name in the message reflects which raw type was detected.","commonSituations":"Migrating from RxJava1 to RxJava2 and dropping generics; suppressing raw-types warnings; copy-paste errors. Surfaces at first method invocation because Retrofit validates lazily.","solutions":["Parameterize the reactive type: `Observable<User> getUser();` (or Flowable/Single/Maybe).","Wrap the body for HTTP metadata: `Observable<Response<User>>` or `Observable<Result<User>>`.","Use `Completable` for no-body endpoints (no parameterization needed)."],"exampleFix":"// before\n@GET(\"users/{id}\")\nObservable getUser(@Path(\"id\") String id);\n\n// after\n@GET(\"users/{id}\")\nObservable<User> getUser(@Path(\"id\") String id);","handlingStrategy":"validation","validationCode":"// javac -Xlint:rawtypes -Werror\nfor (Method m : MyService.class.getDeclaredMethods()) {\n    Class<?> raw = m.getReturnType();\n    if (raw == io.reactivex.Observable.class\n            || raw == io.reactivex.Flowable.class\n            || raw == io.reactivex.Single.class\n            || raw == io.reactivex.Maybe.class) {\n        throw new IllegalStateException(m + \" returns raw \" + raw.getSimpleName() + \"; parameterize it.\");\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    retrofit.create(MyService.class).getUser(\"1\");\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"return type must be parameterized\")) {\n        throw new IllegalStateException(\"Raw RxJava2 type in MyService; parameterize it.\", e);\n    }\n    throw e;\n}","preventionTips":["Treat raw-types warnings as errors.","Only Completable needs no parameter.","Smoke-test each service interface in CI."],"tags":["retrofit","rxjava2","java","reflection","type-erasure","call-adapter"],"analyzedSha":"d0b112dad073b7fe49c953ebc46ff1b424cb1e51","analyzedAt":"2026-08-04T19:12:59.096Z","schemaVersion":2}