lysine-dev/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 RxJava3CallAdapterFactory while resolving the CallAdapter for a service method whose return type's type argument is a raw retrofit2.Response. The factory unwraps the outer reactive type (Observable/Flowable/Single/Maybe) via getParameterUpperBound, and when that bound is Response.class it requires Response itself to carry a concrete body type. A bare Response gives the adapter no body type to deserialize into, so it refuses to build the adapter.

Source

Thrown at retrofit-adapters/rxjava3/src/main/java/retrofit2/adapter/rxjava3/RxJava3CallAdapterFactory.java:137

    Type responseType;
    if (!(returnType instanceof ParameterizedType)) {
      String name =
          isFlowable ? "Flowable" : isSingle ? "Single" : isMaybe ? "Maybe" : "Observable";
      throw new IllegalStateException(
          name
              + " return type must be parameterized"
              + " as "
              + name
              + "<Foo> or "
              + name
              + "<? extends Foo>");
    }

    Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);
    Class<?> rawObservableType = getRawType(observableType);
    if (rawObservableType == Response.class) {
      if (!(observableType instanceof ParameterizedType)) {
        throw new IllegalStateException(
            "Response must be parameterized" + " as Response<Foo> or Response<? extends Foo>");
      }
      responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
    } else if (rawObservableType == Result.class) {
      if (!(observableType instanceof ParameterizedType)) {
        throw new IllegalStateException(
            "Result must be parameterized" + " as Result<Foo> or Result<? extends Foo>");
      }
      responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
      isResult = true;
    } else {
      responseType = observableType;
      isBody = true;
    }

    return new RxJava3CallAdapter(
        responseType, scheduler, isAsync, isResult, isBody, isFlowable, isSingle, isMaybe, false);
  }

View on GitHub (pinned to d0b112dad0)

Solutions

  1. Parameterize the Response in the return type, e.g. change Observable<Response> to Observable<Response<User>>.
  2. If you want the body only, drop Response entirely and return Observable<User>.
  3. If you want both body and errors as a stream, switch to Observable<Result<User>> which the factory also supports.
  4. Clean/rebuild the project so the corrected interface is recompiled before Retrofit.validateEagerly() inspects it.

Example fix

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

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

Strategy: validation

Validate before calling

// Verify the service method signature at build time before Retrofit scans it.
// In Java there is no runtime pre-check; the guard is compile-time discipline:
//   - never write Observable<Response> / Flowable<Response> / Single<Response> / Maybe<Response>
//   - always supply the body type: Observable<Response<BodyType>>
//
// Optional unit test that fails fast if a signature drifts to raw Response:
Method m = ApiService.class.getMethod("getUser", String.class);
Type rt = m.getGenericReturnType();
if (rt instanceof ParameterizedType) {
  Type arg = ((ParameterizedType) rt).getActualTypeArguments()[0];
  if (arg == retrofit2.Response.class) {
    throw new AssertionError("getUser() returns raw Observable<Response>; parameterize it.");
  }
}

Type guard

// Kotlin reified helper to enforce Response is parameterized at the call site.
inline fun <reified T> responseObservableType(): Type =
    TypeToken<Observable<Response<T>>>() {}.type  // compile error if T omitted

Try / catch

// This error fires at Retrofit.create()/build() time, not per-call.
// Prefer fixing the signature; if you must isolate it, catch during service creation:
try {
  ApiService api = retrofit.create(ApiService.class);
} catch (IllegalStateException e) {
  if (e.getMessage() != null && e.getMessage().contains("Response must be parameterized")) {
    throw new ConfigurationException("Service interface has a raw Observable<Response> method", e);
  }
  throw e;
}

Prevention

When it happens

Trigger: A Retrofit interface method declared as Observable<Response>, Flowable<Response>, Single<Response>, or Maybe<Response> (raw Response, no generic argument). The factory's get() is invoked at Retrofit.build() time when it scans the service interface; the check `observableType instanceof ParameterizedType` fails for the raw class and throws IllegalStateException.

Common situations: Copying a method signature and forgetting to parameterize Response, refactoring a method that returned Observable<User> into Observable<Response> to gain access to headers/status, or IDE auto-import picking the raw retrofit2.Response type and silently dropping the generic.

Related errors


AI-assisted analysis of lysine-dev/retrofit@d0b112dad0 (2026-08-13). Data as JSON: /api/errors/b0485cf41357d9f9. Report an issue: GitHub.