square/retrofit · error · NullPointerException
scheduler == null
Error message
scheduler == null
What it means
`RxJava3CallAdapterFactory.createWithScheduler(Scheduler)` at RxJava3CallAdapterFactory.java:86 requires a non-null Scheduler because every stream produced will subscribeOn it. A null scheduler would NPE inside RxJava 3 internals, so the factory fails fast at construction with a clear message.
Source
Thrown at retrofit-adapters/rxjava3/src/main/java/retrofit2/adapter/rxjava3/RxJava3CallAdapterFactory.java:86
return new RxJava3CallAdapterFactory(null, true);
}
/**
* Returns an instance which creates synchronous observables that do not operate on any scheduler
* by default. Applying {@code subscribeOn(..)} will change the scheduler on which the HTTP calls
* are made.
*/
public static RxJava3CallAdapterFactory createSynchronous() {
return new RxJava3CallAdapterFactory(null, false);
}
/**
* Returns an instance which creates synchronous observables that {@code subscribeOn(..)} the
* supplied {@code scheduler} by default.
*/
@SuppressWarnings("ConstantConditions") // Guarding public API nullability.
public static RxJava3CallAdapterFactory createWithScheduler(Scheduler scheduler) {
if (scheduler == null) throw new NullPointerException("scheduler == null");
return new RxJava3CallAdapterFactory(scheduler, false);
}
private final @Nullable Scheduler scheduler;
private final boolean isAsync;
private RxJava3CallAdapterFactory(@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: `RxJava3CallAdapterFactory.createWithScheduler(io.reactivex.rxjava3.core.Schedulers.io())`.
- If you do not need a default scheduler, use `create()` (async default) or `createSynchronous()`.
- Initialize the scheduler before constructing the factory.
Example fix
// before
Retrofit r = new Retrofit.Builder()
.addCallAdapterFactory(RxJava3CallAdapterFactory.createWithScheduler(resolveScheduler()))
.build(); // null -> NPE
// after
Scheduler s = resolveScheduler();
if (s == null) s = io.reactivex.rxjava3.core.Schedulers.io();
Retrofit r = new Retrofit.Builder()
.addCallAdapterFactory(RxJava3CallAdapterFactory.createWithScheduler(s))
.build(); Defensive patterns
Strategy: validation
Validate before calling
Scheduler s = resolveSchedulerFromConfig();
if (s == null) throw new IllegalArgumentException("scheduler config missing");
RxJava3CallAdapterFactory factory = RxJava3CallAdapterFactory.createWithScheduler(s); Try / catch
RxJava3CallAdapterFactory factory;
try {
factory = RxJava3CallAdapterFactory.createWithScheduler(resolveScheduler());
} catch (NullPointerException e) {
if (e.getMessage().equals("scheduler == null")) {
factory = RxJava3CallAdapterFactory.create();
} else throw e;
} Prevention
- Validate scheduler config before factory construction.
- Note RxJava3 defaults to async (create()); use createSynchronous() if you need sync.
- Unit-test wiring to catch null schedulers.
When it happens
Trigger: Calling `RxJava3CallAdapterFactory.createWithScheduler(null)` while building the Retrofit instance.
Common situations: Passing an uninitialized Scheduler field; resolving a scheduler from config that returned null; refactoring from create()/createSynchronous() and forgetting the argument.
Related errors
AI-assisted analysis of square/retrofit@d0b112dad0 (2026-08-04).
Data as JSON: /data/errors/2d0f5ebf75d59642.json.
Report an issue: GitHub.