square/retrofit · error · NullPointerException
scheduler == null
Error message
scheduler == null
What it means
`RxJava2CallAdapterFactory.createWithScheduler(Scheduler)` at RxJava2CallAdapterFactory.java:81 requires a non-null Scheduler because every stream produced will subscribeOn it. A null value would NPE deep inside RxJava 2 internals, so the factory fails fast at construction with a clear message.
Source
Thrown at retrofit-adapters/rxjava2/src/main/java/retrofit2/adapter/rxjava2/RxJava2CallAdapterFactory.java:81
* Returns an instance which creates synchronous observables that do not operate on any scheduler
* by default.
*/
public static RxJava2CallAdapterFactory create() {
return new RxJava2CallAdapterFactory(null, false);
}
/** Returns an instance which creates asynchronous observables. */
public static RxJava2CallAdapterFactory createAsync() {
return new RxJava2CallAdapterFactory(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 RxJava2CallAdapterFactory createWithScheduler(Scheduler scheduler) {
if (scheduler == null) throw new NullPointerException("scheduler == null");
return new RxJava2CallAdapterFactory(scheduler, false);
}
private final @Nullable Scheduler scheduler;
private final boolean isAsync;
private RxJava2CallAdapterFactory(@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);
if (rawType == Completable.class) {
// Completable is not parameterized (which is what the rest of this method deals with) so itView on GitHub (pinned to d0b112dad0)
Solutions
- Pass a concrete Scheduler: `RxJava2CallAdapterFactory.createWithScheduler(io.reactivex.schedulers.Schedulers.io())`.
- If you do not need a default scheduler, use `create()` or `createAsync()`.
- Initialize the scheduler before constructing the factory.
Example fix
// before
Retrofit r = new Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(resolveScheduler()))
.build(); // null -> NPE
// after
Scheduler s = resolveScheduler();
if (s == null) s = io.reactivex.schedulers.Schedulers.io();
Retrofit r = new Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(s))
.build(); Defensive patterns
Strategy: validation
Validate before calling
Scheduler s = resolveSchedulerFromConfig();
if (s == null) throw new IllegalArgumentException("scheduler config missing");
RxJava2CallAdapterFactory factory = RxJava2CallAdapterFactory.createWithScheduler(s); Try / catch
RxJava2CallAdapterFactory factory;
try {
factory = RxJava2CallAdapterFactory.createWithScheduler(resolveScheduler());
} catch (NullPointerException e) {
if (e.getMessage().equals("scheduler == null")) {
factory = RxJava2CallAdapterFactory.create();
} else throw e;
} Prevention
- Validate scheduler config before factory construction.
- Prefer create()/createAsync() unless you need subscribeOn by default.
- Unit-test your wiring to catch null schedulers.
When it happens
Trigger: Calling `RxJava2CallAdapterFactory.createWithScheduler(null)` while building the Retrofit instance.
Common situations: Passing an uninitialized Scheduler field; resolving a scheduler from config that came back null; or refactoring from create() and forgetting the argument.
Related errors
AI-assisted analysis of square/retrofit@d0b112dad0 (2026-08-04).
Data as JSON: /data/errors/04e5490b8fd7b5e1.json.
Report an issue: GitHub.