square/retrofit · error · IllegalStateException

Response must be parameterized as Response<Foo> or Response<

Error message

Response must be parameterized as Response<Foo> or Response<? extends Foo>

What it means

Thrown by GuavaCallAdapterFactory when the future IS parameterized, but its single type argument is the raw `Response` class (e.g. `ListenableFuture<Response>`). The factory detected Response as the inner type and then needs to extract the body type T from `Response<T>`; a raw Response leaves T unknown, so at GuavaCallAdapterFactory.java:80 it throws.

Source

Thrown at retrofit-adapters/guava/src/main/java/retrofit2/adapter/guava/GuavaCallAdapterFactory.java:81

      Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != ListenableFuture.class) {
      return null;
    }
    if (!(returnType instanceof ParameterizedType)) {
      throw new IllegalStateException(
          "ListenableFuture return type must be parameterized"
              + " as ListenableFuture<Foo> or ListenableFuture<? extends Foo>");
    }
    Type innerType = getParameterUpperBound(0, (ParameterizedType) returnType);

    if (getRawType(innerType) != Response.class) {
      // Generic type is not Response<T>. Use it for body-only adapter.
      return new BodyCallAdapter<>(innerType);
    }

    // Generic type is Response<T>. Extract T and create the Response version of the adapter.
    if (!(innerType instanceof ParameterizedType)) {
      throw new IllegalStateException(
          "Response must be parameterized" + " as Response<Foo> or Response<? extends Foo>");
    }
    Type responseType = getParameterUpperBound(0, (ParameterizedType) innerType);
    return new ResponseCallAdapter<>(responseType);
  }

  private static final class BodyCallAdapter<R> implements CallAdapter<R, ListenableFuture<R>> {
    private final Type responseType;

    BodyCallAdapter(Type responseType) {
      this.responseType = responseType;
    }

    @Override
    public Type responseType() {
      return responseType;
    }

View on GitHub (pinned to d0b112dad0)

Solutions

  1. Parameterize the inner Response with the body type: `ListenableFuture<Response<User>> getUser();`.
  2. If you do not actually need the Response wrapper, drop it and return `ListenableFuture<User>` directly.
  3. Rebuild; the factory will resolve responseType and return a ResponseCallAdapter.

Example fix

// before
@GET("users/{id}")
ListenableFuture<Response> getUser(@Path("id") String id);

// after
@GET("users/{id}")
ListenableFuture<Response<User>> getUser(@Path("id") String id);
Defensive patterns

Strategy: validation

Validate before calling

// Scan service methods for a raw Response nested inside a future type.
for (Method m : MyService.class.getDeclaredMethods()) {
    Type rt = m.getGenericReturnType();
    if (rt instanceof ParameterizedType
            && ((ParameterizedType) rt).getRawType() == retrofit2.Response.class) {
        throw new IllegalStateException(m + " uses raw Response; parameterize Response<T>.");
    }
    // also handle Response nested one level deep, e.g. ListenableFuture<Response>
}

Try / catch

// Programming error -- do not silently swallow. Fail fast in a wiring test:
try {
    retrofit.create(MyService.class).getUser("1");
} catch (IllegalStateException e) {
    if (e.getMessage().contains("Response must be parameterized")) {
        throw new IllegalStateException("Fix raw Response<> in MyService.", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: A service method returns `ListenableFuture<Response>` (raw Response) rather than `ListenableFuture<Response<Foo>>`. The check `innerType instanceof ParameterizedType` at line 80 fails because Response has no type arguments.

Common situations: Wrapping a return in Response to get status/headers but forgetting to also parameterize Response with the body type; refactoring a body return into a Response return and losing the inner generic; or autocompleting `Response` without its argument.

Related errors


AI-assisted analysis of square/retrofit@d0b112dad0 (2026-08-04). Data as JSON: /data/errors/a5876cd250f1c2d0.json. Report an issue: GitHub.