lysine-dev/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
Retrofit's RxJava 1.x adapter supports Observable, Single, and Completable return types. Observable and Single must carry a generic element type; a raw type leaves the adapter unable to resolve the emitted item type, so RxJavaCallAdapterFactory throws IllegalStateException, interpolating the actual raw class name ('Single' or 'Observable') into the message. Completable is exempt because it has no 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
- Parameterize the return type: `Observable<List<User>> getUsers();` or `Single<User> findUser(@Path("id") long id);`
- For fire-and-forget calls with no payload use Completable: `Completable deleteUser(@Path("id") long id);`
- For full response or error packaging use `Observable<Response<User>>` or `Observable<Result<User>>`.
Example fix
// before Observable getUsers(); // after Observable<List<User>> getUsers();
Defensive patterns
Strategy: validation
Validate before calling
import java.lang.reflect.*;
import rx.*;
static void assertRxTypesParameterized(Class<?> iface) {
for (Method m : iface.getDeclaredMethods()) {
Type rt = m.getGenericReturnType();
Class<?> raw = rt instanceof Class ? (Class<?>) rt : getRaw(rt);
if ((raw == Observable.class || raw == Single.class) && !(rt instanceof ParameterizedType)) {
throw new AssertionError("Raw " + raw.getSimpleName() + " return type on " + m);
}
}
} Try / catch
try {
MyService svc = retrofit.create(MyService.class);
Observable<List<User>> o = svc.getUsers();
} catch (IllegalStateException e) {
throw new IllegalStateException("RxJava return type is raw on the service interface: " + e.getMessage(), e);
} Prevention
- Build with -Xlint:rawtypes so raw Observable/Single fail fast.
- Prefer Completable for no-payload methods so no generic is needed.
- Add a reflection unit test over the service interface.
When it happens
Trigger: A method with a raw return type such as `Observable getUsers()` or `Single findUser()`. Thrown in get() when `returnType` is not a ParameterizedType and the type is not Completable; the `name` variable is set to 'Single' when isSingle else 'Observable'.
Common situations: Migrating from `Call<T>` to RxJava and omitting the generic; mixing RxJava 1 and 2 adapter types; suppress raw-types warnings.
Related errors
- Response must be parameterized as Response<Foo> or Response<
- Result must be parameterized as Result<Foo> or Result<? exte
- ${name} return type must be parameterized as ${name}<Foo> or
- ${name} return type must be parameterized as ${name}<Foo> or
- ListenableFuture return type must be parameterized as Listen
AI-assisted analysis of lysine-dev/retrofit@d0b112dad0 (2026-08-13).
Data as JSON: /api/errors/13611b65a6eeaf02.
Report an issue: GitHub.