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

  1. Pass a real Callable: RxAndroidPlugins.initMainThreadScheduler(() -> Schedulers.trampoline()) — if your goal was overriding the scheduler, prefer RxAndroidPlugins.setInitMainThreadSchedulerHandler(callable -> Schedulers.trampoline()) instead.
  2. Add a null check in your setup helper before calling the API and fail with a descriptive message.
  3. In Kotlin, make the parameter non-null (Callable<Scheduler> instead of Callable<Scheduler>?) so the compiler rejects the call.
  4. 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

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


AI-assisted analysis of ReactiveX/RxAndroid@afaea28046 (2026-08-14). Data as JSON: /api/errors/b82739bd5bddb9a5. Report an issue: GitHub.