square/retrofit · error · IllegalStateException

${name} return type must be parameterized as ${name}<Foo> or

Error message

${name} return type must be parameterized as ${name}<Foo> or ${name}<? extends Foo>

What it means

Thrown by RxJavaCallAdapterFactory while validating a service method's return type. The placeholder `${name}` resolves to `Single` or `Observable` depending on the raw type detected (set at RxJavaCallAdapterFactory.java:113). A raw reactive type gives the factory no body type, so it cannot pick a converter; the check at line 112 (`returnType instanceof ParameterizedType`) rejects it. (Completable is handled separately and does not need a type parameter.)

Source

Thrown at retrofit-adapters/rxjava/src/main/java/retrofit2/adapter/rxjava/RxJavaCallAdapterFactory.java:114

  public @Nullable CallAdapter<?, ?> get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {
    Class<?> rawType = getRawType(returnType);
    boolean isSingle = rawType == Single.class;
    boolean isCompletable = rawType == Completable.class;
    if (rawType != Observable.class && !isSingle && !isCompletable) {
      return null;
    }

    if (isCompletable) {
      return new RxJavaCallAdapter(Void.class, scheduler, isAsync, false, true, false, true);
    }

    boolean isResult = false;
    boolean isBody = false;
    Type responseType;
    if (!(returnType instanceof ParameterizedType)) {
      String name = isSingle ? "Single" : "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) {

View on GitHub (pinned to d0b112dad0)

Solutions

  1. Parameterize the reactive type: `Observable<User> getUser();` or `Single<User> getUser();`.
  2. For HTTP metadata, wrap the body: `Observable<Response<User>>` or `Observable<Result<User>>`.
  3. Use `Completable` for fire-and-forget endpoints with no body (no parameterization needed).

Example fix

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

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

Strategy: validation

Validate before calling

// Compile-time: javac -Xlint:rawtypes -Werror.
// Reflection guard covering Observable and Single:
for (Method m : MyService.class.getDeclaredMethods()) {
    Class<?> raw = m.getReturnType();
    if (raw == rx.Observable.class || raw == rx.Single.class) {
        throw new IllegalStateException(m + " returns raw " + raw.getSimpleName() + "; parameterize it.");
    }
}

Try / catch

// Surface it loudly in a wiring test:
try {
    retrofit.create(MyService.class).getUser("1");
} catch (IllegalStateException e) {
    if (e.getMessage().contains("return type must be parameterized")) {
        throw new IllegalStateException("Raw Observable/Single in MyService; parameterize it.", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: A service method returns a raw `Observable` or raw `Single` (RxJava 1.x), e.g. `@GET("/") Observable getUser();`. name becomes 'Observable' or 'Single' in the message.

Common situations: Migrating to RxJava call adapters and forgetting the generic; suppressing raw-types warnings; or copying a method and dropping `<User>`. Surfaces at first invocation of the method because Retrofit validates lazily.

Related errors


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