{"id":"ab200ed523a7b23a","repo":"square/retrofit","slug":"completablefuture-return-type-must-be-parameterize","errorCode":null,"errorMessage":"CompletableFuture return type must be parameterized as CompletableFuture<Foo> or CompletableFuture<? extends Foo>","messagePattern":"CompletableFuture return type must be parameterized as CompletableFuture<Foo> or CompletableFuture<\\? extends Foo>","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"retrofit-adapters/java8/src/main/java/retrofit2/adapter/java8/Java8CallAdapterFactory.java","lineNumber":67,"sourceCode":" *           errors\n *     </ul>\n */\n@Deprecated\npublic final class Java8CallAdapterFactory extends CallAdapter.Factory {\n  public static Java8CallAdapterFactory create() {\n    return new Java8CallAdapterFactory();\n  }\n\n  private Java8CallAdapterFactory() {}\n\n  @Override\n  public @Nullable CallAdapter<?, ?> get(\n      Type returnType, Annotation[] annotations, Retrofit retrofit) {\n    if (getRawType(returnType) != CompletableFuture.class) {\n      return null;\n    }\n    if (!(returnType instanceof ParameterizedType)) {\n      throw new IllegalStateException(\n          \"CompletableFuture return type must be parameterized\"\n              + \" as CompletableFuture<Foo> or CompletableFuture<? extends Foo>\");\n    }\n    Type innerType = getParameterUpperBound(0, (ParameterizedType) returnType);\n\n    if (getRawType(innerType) != Response.class) {\n      // Generic type is not Response<T>. Use it for body-only adapter.\n      return new BodyCallAdapter<>(innerType);\n    }\n\n    // Generic type is Response<T>. Extract T and create the Response version of the adapter.\n    if (!(innerType instanceof ParameterizedType)) {\n      throw new IllegalStateException(\n          \"Response must be parameterized\" + \" as Response<Foo> or Response<? extends Foo>\");\n    }\n    Type responseType = getParameterUpperBound(0, (ParameterizedType) innerType);\n    return new ResponseCallAdapter<>(responseType);\n  }","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/square/retrofit/blob/d0b112dad073b7fe49c953ebc46ff1b424cb1e51/retrofit-adapters/java8/src/main/java/retrofit2/adapter/java8/Java8CallAdapterFactory.java#L49-L85","documentation":"Thrown by the (now-deprecated) Java8CallAdapterFactory while inspecting a service method's return type. A raw `CompletableFuture` gives the factory no body type to deserialize into, so at Java8CallAdapterFactory.java:67 it rejects the raw type. Note: Retrofit now bundles CompletableFuture support, so adding this factory explicitly is unnecessary.","triggerScenarios":"A service interface method returns raw `CompletableFuture` (no type argument): `@GET(\"/\") CompletableFuture getData();`. The check `returnType instanceof ParameterizedType` at line 67 fails.","commonSituations":"Migrating to Java 8 futures and forgetting the generic; or explicitly adding the deprecated Java8CallAdapterFactory while writing a first method. Using a raw type to silence a compiler warning. Surfaces on first method invocation because Retrofit validates lazily.","solutions":["Parameterize the return: `CompletableFuture<User> getUser();`.","Use `CompletableFuture<Response<User>>` if you need the full HTTP Response.","Since bundled support exists, remove the explicit Java8CallAdapterFactory from Retrofit.Builder unless you depend on the deprecated class -- Retrofit handles CompletableFuture natively."],"exampleFix":"// before\n@GET(\"users/{id}\")\nCompletableFuture getUser(@Path(\"id\") String id);\n\n// after\n@GET(\"users/{id}\")\nCompletableFuture<User> getUser(@Path(\"id\") String id);","handlingStrategy":"validation","validationCode":"// javac -Xlint:rawtypes -Werror catches raw CompletableFuture at compile time.\n// Reflection guard for completeness:\nfor (Method m : MyService.class.getDeclaredMethods()) {\n    if (m.getGenericReturnType() == java.util.concurrent.CompletableFuture.class) {\n        throw new IllegalStateException(m + \" returns raw CompletableFuture.\");\n    }\n}","typeGuard":null,"tryCatchPattern":"// Fail fast during a wiring/self-test rather than at production call time:\ntry {\n    retrofit.create(MyService.class).getUser(\"1\");\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"CompletableFuture return type\")) {\n        throw new IllegalStateException(\"Raw CompletableFuture in MyService; parameterize it.\", e);\n    }\n    throw e;\n}","preventionTips":["Do not add the deprecated Java8CallAdapterFactory -- rely on built-in support.","Never suppress raw-types warnings on Retrofit return types.","Write a smoke test per service interface that invokes one method."],"tags":["retrofit","java8","java","reflection","type-erasure","deprecated"],"analyzedSha":"d0b112dad073b7fe49c953ebc46ff1b424cb1e51","analyzedAt":"2026-08-04T19:12:59.096Z","schemaVersion":2}