lysine-dev/retrofit · error · IllegalStateException
Result must be parameterized as Result<Foo> or Result<? exte
Error message
Result must be parameterized as Result<Foo> or Result<? extends Foo>
What it means
The RxJava 1.x adapter's Result<T> wrapper packages either a Response or an error into the stream so that network failures are delivered as data rather than via onError. A raw Result (Observable<Result>) gives the adapter no body type to deserialize, so RxJavaCallAdapterFactory throws IllegalStateException when the inner observableType is Result.class but not a ParameterizedType.
Source
Thrown at retrofit-adapters/rxjava/src/main/java/retrofit2/adapter/rxjava/RxJavaCallAdapterFactory.java:134
+ " 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 RxJavaCallAdapter(
responseType, scheduler, isAsync, isResult, isBody, isSingle, false);
}
}
View on GitHub (pinned to d0b112dad0)
Solutions
- Parameterize Result: `Observable<Result<User>> getUser(@Path("id") long id);`
- Switch to Response or body-only if you do not need error-in-stream semantics.
Example fix
// before
Observable<Result> getUser(@Path("id") long id);
// after
Observable<Result<User>> getUser(@Path("id") long id); Defensive patterns
Strategy: validation
Validate before calling
import java.lang.reflect.*;
static void assertRxResultParameterized(Class<?> iface) {
for (Method m : iface.getDeclaredMethods()) {
if (!(m.getGenericReturnType() instanceof ParameterizedType)) continue;
Type inner = ((ParameterizedType) m.getGenericReturnType()).getActualTypeArguments()[0];
if (inner.getTypeName().startsWith("retrofit2.adapter.rxjava.Result") && !(inner instanceof ParameterizedType)) {
throw new AssertionError("Raw nested Result on " + m);
}
}
} Try / catch
try {
Observable<Result<User>> o = service.getUser(id);
} catch (IllegalStateException e) {
throw new IllegalStateException("Nested Result return type is raw: " + e.getMessage(), e);
} Prevention
- Fail builds on raw types (-Xlint:rawtypes).
- Unit-test that nested Result types are parameterized.
- Remember Result is itself generic, not a marker class.
When it happens
Trigger: A method like `Observable<Result> getUser();` — Result used without its type argument. Thrown after `getRawType(observableType) == Result.class` when observableType is not a ParameterizedType.
Common situations: Using Result to swallow network errors in-stream but forgetting the body generic; copy-paste from samples.
Related errors
- ${name} return type must be parameterized as ${name}<Foo> or
- Response must be parameterized as Response<Foo> or Response<
- Result must be parameterized as Result<Foo> or Result<? exte
- ListenableFuture return type must be parameterized as Listen
- Response must be parameterized as Response<Foo> or Response<
AI-assisted analysis of lysine-dev/retrofit@d0b112dad0 (2026-08-13).
Data as JSON: /api/errors/da51ea375b8d1ca1.
Report an issue: GitHub.