ReactiveX/RxAndroid · error · NullPointerException
scheduler == null
Error message
scheduler == null
What it means
RxAndroidPlugins.initMainThreadScheduler(Callable<Scheduler>) throws this NullPointerException when the Callable argument itself is null. The method is the plugin entry point that AndroidSchedulers.main() uses to resolve the main-thread scheduler, and it refuses null input because there would be no way to construct a Scheduler. In normal library usage the Callable is an internal constant, so this NPE almost always means application or test code called the plugins API directly with null.
Source
Thrown at rxandroid/src/main/java/io/reactivex/rxjava3/android/plugins/RxAndroidPlugins.java:35
import io.reactivex.rxjava3.core.Scheduler;
import io.reactivex.rxjava3.exceptions.Exceptions;
import io.reactivex.rxjava3.functions.Function;
/**
* Utility class to inject handlers to certain standard RxAndroid operations.
*/
public final class RxAndroidPlugins {
private static volatile Function<Callable<Scheduler>, Scheduler> onInitMainThreadHandler;
private static volatile Function<Scheduler, Scheduler> onMainThreadHandler;
public static void setInitMainThreadSchedulerHandler(Function<Callable<Scheduler>, Scheduler> handler) {
onInitMainThreadHandler = handler;
}
public static Scheduler initMainThreadScheduler(Callable<Scheduler> scheduler) {
if (scheduler == null) {
throw new NullPointerException("scheduler == null");
}
Function<Callable<Scheduler>, Scheduler> f = onInitMainThreadHandler;
if (f == null) {
return callRequireNonNull(scheduler);
}
return applyRequireNonNull(f, scheduler);
}
public static void setMainThreadSchedulerHandler(Function<Scheduler, Scheduler> handler) {
onMainThreadHandler = handler;
}
public static Scheduler onMainThreadScheduler(Scheduler scheduler) {
if (scheduler == null) {
throw new NullPointerException("scheduler == null");
}
Function<Scheduler, Scheduler> f = onMainThreadHandler;
if (f == null) {View on GitHub (pinned to afaea28046)
Solutions
- Pass a real Callable: RxAndroidPlugins.initMainThreadScheduler(() -> Schedulers.trampoline()) — if your goal was overriding the scheduler, prefer RxAndroidPlugins.setInitMainThreadSchedulerHandler(callable -> Schedulers.trampoline()) instead.
- Add a null check in your setup helper before calling the API and fail with a descriptive message.
- In Kotlin, make the parameter non-null (Callable<Scheduler> instead of Callable<Scheduler>?) so the compiler rejects the call.
- Fix mock stubbing: when(mockCallable.call()).thenReturn(scheduler), never thenReturn(null).
Example fix
// before RxAndroidPlugins.initMainThreadScheduler(null); // NPE: scheduler == null // after RxAndroidPlugins.setInitMainThreadSchedulerHandler(callable -> Schedulers.trampoline());
Defensive patterns
Strategy: validation
Validate before calling
if (callable == null) {
throw new IllegalArgumentException("scheduler callable required");
}
RxAndroidPlugins.initMainThreadScheduler(callable); Type guard
public static boolean isUsableSchedulerCallable(Callable<Scheduler> c) {
if (c == null) return false;
try { return c.call() != null; } catch (Exception e) { return false; }
} Try / catch
try {
RxAndroidPlugins.initMainThreadScheduler(callable);
} catch (NullPointerException e) {
if ("scheduler == null".equals(e.getMessage())) {
throw new IllegalStateException("Scheduler bootstrap misconfigured: callable missing", e);
}
throw e;
} Prevention
- Prefer the handler API (setInitMainThreadSchedulerHandler) for overrides; never pass a bare null to plugin methods.
- In Kotlin, type scheduler inputs as non-null so the compiler rejects null at the call site.
- Centralize all RxAndroidPlugins configuration in one bootstrap class instead of scattering direct plugin calls.
When it happens
Trigger: Calling RxAndroidPlugins.initMainThreadScheduler(null) directly; a helper that wraps scheduler setup and passes a nullable variable that happened to be null; mocking frameworks returning null for the Callable in tests; reflective or copy-pasted initialization code that drops the argument.
Common situations: Test bootstrap code that intends to override the main scheduler but passes null instead of a Callable (e.g. RxAndroidPlugins.initMainThreadScheduler(null) instead of setInitMainThreadSchedulerHandler(callable -> Schedulers.trampoline())); Kotlin code where a Scheduler? or Callable<Scheduler>? leaks into the call; partial mocks in unit tests whose default stubbing returns null.
Related errors
- Scheduler Callable returned null
- looper == null
- run == null
- Expected to be called on the main thread but was {threadName
AI-assisted analysis of ReactiveX/RxAndroid@afaea28046 (2026-08-14).
Data as JSON: /api/errors/b82739bd5bddb9a5.
Report an issue: GitHub.