lysine-dev/retrofit · error · NullPointerException
scheduler == null
Error message
scheduler == null
What it means
RxJavaCallAdapterFactory.createWithScheduler(Scheduler) builds an adapter factory that subscribeOn's the supplied scheduler by default. Passing null is rejected because the factory would then hold no scheduler to apply; for the no-scheduler case the library provides dedicated factory methods (create() for synchronous, createAsync() for asynchronous). It throws NullPointerException ('scheduler == null') behind a ConstantConditions suppression.
Source
Thrown at retrofit-adapters/rxjava/src/main/java/retrofit2/adapter/rxjava/RxJavaCallAdapterFactory.java:83
* Returns an instance which creates synchronous observables that do not operate on any scheduler
* by default.
*/
public static RxJavaCallAdapterFactory create() {
return new RxJavaCallAdapterFactory(null, false);
}
/** Returns an instance which creates asynchronous observables. */
public static RxJavaCallAdapterFactory createAsync() {
return new RxJavaCallAdapterFactory(null, true);
}
/**
* Returns an instance which creates synchronous observables that {@linkplain
* Observable#subscribeOn(Scheduler) subscribe on} {@code scheduler} by default.
*/
@SuppressWarnings("ConstantConditions") // Guarding public API nullability.
public static RxJavaCallAdapterFactory createWithScheduler(Scheduler scheduler) {
if (scheduler == null) throw new NullPointerException("scheduler == null");
return new RxJavaCallAdapterFactory(scheduler, false);
}
private final @Nullable Scheduler scheduler;
private final boolean isAsync;
private RxJavaCallAdapterFactory(@Nullable Scheduler scheduler, boolean isAsync) {
this.scheduler = scheduler;
this.isAsync = isAsync;
}
@Override
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) {View on GitHub (pinned to d0b112dad0)
Solutions
- If you want no custom scheduler, use the dedicated factory: RxJavaCallAdapterFactory.create()() or RxJavaCallAdapterFactory.createAsync().
- Otherwise supply a real Scheduler such as Schedulers.io(), Schedulers.computation(), or AndroidSchedulers.mainThread().
- Resolve the null at its source (DI binding / config property) rather than passing it through.
Example fix
// before
RxJavaCallAdapterFactory factory = RxJavaCallAdapterFactory.createWithScheduler(configScheduler);
// after
RxJavaCallAdapterFactory factory = configScheduler != null
? RxJavaCallAdapterFactory.createWithScheduler(configScheduler)
: RxJavaCallAdapterFactory.createAsync(); Defensive patterns
Strategy: validation
Validate before calling
Scheduler s = resolveScheduler(); // may be null when unconfigured
RxJavaCallAdapterFactory factory =
s != null
? RxJavaCallAdapterFactory.createWithScheduler(s)
: RxJavaCallAdapterFactory.createAsync(); Try / catch
try {
factory = RxJavaCallAdapterFactory.createWithScheduler(s);
} catch (NullPointerException e) {
// s was null; fall back to the async factory
factory = RxJavaCallAdapterFactory.createAsync();
} Prevention
- Prefer the no-arg factory methods (createAsync / create()) unless you specifically need subscribeOn.
- Bind a non-null Scheduler in DI and fail fast at startup if it is missing.
- In tests, inject Schedulers.trampoline() rather than null.
When it happens
Trigger: Calling RxJavaCallAdapterFactory.createWithScheduler(null), most often when the scheduler is read from DI/configuration/env that was not bound and resolved to null. Thrown immediately at the top of createWithScheduler.
Common situations: Reading a Scheduler from a DI graph or config property that resolved to null; conditional scheduler selection that falls through to null; copy-paste of createWithScheduler without choosing the right base factory method; environment differences (e.g. Android main thread scheduler absent in unit tests).
Related errors
- scheduler == null
- scheduler == null
- error == null
- response == null
- ${name} return type must be parameterized as ${name}<Foo> or
AI-assisted analysis of lysine-dev/retrofit@d0b112dad0 (2026-08-13).
Data as JSON: /api/errors/f3a13de941ae2553.
Report an issue: GitHub.