{"record":{"id":"2724741f623dadc3","repo":"ReactiveX/RxAndroid","slug":"scheduler-callable-returned-null","errorCode":null,"errorMessage":"Scheduler Callable returned null","messagePattern":"Scheduler Callable returned null","errorType":"exception","errorClass":"NullPointerException","httpStatus":null,"severity":"error","filePath":"rxandroid/src/main/java/io/reactivex/rxjava3/android/plugins/RxAndroidPlugins.java","lineNumber":87,"sourceCode":"     * @return the hook function, may be null\n     */\n    public static Function<Scheduler, Scheduler> getOnMainThreadSchedulerHandler() {\n        return onMainThreadHandler;\n    }\n\n    /**\n     * Removes all handlers and resets the default behavior.\n     */\n    public static void reset() {\n        setInitMainThreadSchedulerHandler(null);\n        setMainThreadSchedulerHandler(null);\n    }\n\n    static Scheduler callRequireNonNull(Callable<Scheduler> s) {\n        try {\n            Scheduler scheduler = s.call();\n            if (scheduler == null) {\n                throw new NullPointerException(\"Scheduler Callable returned null\");\n            }\n            return scheduler;\n        } catch (Throwable ex) {\n            throw Exceptions.propagate(ex);\n        }\n    }\n\n    static Scheduler applyRequireNonNull(Function<Callable<Scheduler>, Scheduler> f, Callable<Scheduler> s) {\n        Scheduler scheduler = apply(f,s);\n        if (scheduler == null) {\n            throw new NullPointerException(\"Scheduler Callable returned null\");\n        }\n        return scheduler;\n    }\n\n    static <T, R> R apply(Function<T, R> f, T t) {\n        try {\n            return f.apply(t);","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/ReactiveX/RxAndroid/blob/afaea280461456b30e6ea608428517476ddb8b39/rxandroid/src/main/java/io/reactivex/rxjava3/android/plugins/RxAndroidPlugins.java#L69-L105","documentation":"callRequireNonNull is the default path of RxAndroidPlugins.initMainThreadScheduler: it invokes the Scheduler Callable and throws this NullPointerException when call() returns null. The message 'Scheduler Callable returned null' distinguishes it from a null Callable argument — the Callable ran but produced nothing. This is RxAndroid's equivalent of RxJava's plugin contract: scheduler factories must never yield null.","triggerScenarios":"A custom Callable passed to (or reached via) initMainThreadScheduler whose call() returns null, e.g. because a lazily-created HandlerScheduler was never assigned; a mocked Callable stubbed with thenReturn(null); a Kotlin lambda like { null } inferred as Callable<Scheduler?>; AndroidSchedulers.from(...) invoked with a null Looper inside the Callable (the NPE propagates through Exceptions.propagate with a different message).","commonSituations":"Test setup that mocks the scheduler callable but forgets to stub call(); a test double whose default answer returns null (Mockito RETURNS_DEFAULTS); initialization code that reads a scheduler from a nullable map or DI container that has not been populated yet; refactoring where the Callable body loses its return statement's value.","solutions":["Make the Callable return a real scheduler: () -> Schedulers.trampoline() for tests, or () -> AndroidSchedulers.from(Looper.getMainLooper()) for runtime.","Fix the mock: when(callable.call()).thenReturn(testScheduler) — never leave call() unstubbed.","Add Objects.requireNonNull inside your Callable so construction fails loudly at the true source: () -> Objects.requireNonNull(buildScheduler(), \"buildScheduler\").","If a Throwable from call() is being rethrown, note callRequireNonNull wraps it via Exceptions.propagate — inspect the cause chain in the stack trace."],"exampleFix":"// before\nRxAndroidPlugins.setInitMainThreadSchedulerHandler(c -> null); // handler path\n// or a Callable returning null:\nCallable<Scheduler> c = () -> maybeScheduler; // null field -> NPE \"Scheduler Callable returned null\"\n\n// after\nRxAndroidPlugins.setInitMainThreadSchedulerHandler(c -> Schedulers.trampoline());","handlingStrategy":"validation","validationCode":"Callable<Scheduler> safe = () -> {\n    Scheduler s = buildScheduler();\n    if (s == null) throw new IllegalStateException(\"buildScheduler() returned null\");\n    return s;\n};\nRxAndroidPlugins.initMainThreadScheduler(safe);","typeGuard":null,"tryCatchPattern":"try {\n    return RxAndroidPlugins.initMainThreadScheduler(callable);\n} catch (NullPointerException e) {\n    if (\"Scheduler Callable returned null\".equals(e.getMessage())) {\n        return Schedulers.trampoline(); // safe default rather than crashing bootstrap\n    }\n    throw e;\n}","preventionTips":["Never let a scheduler factory return null; throw a descriptive exception from inside the Callable instead.","Stub every mocked Callable/Scheduler: when(callable.call()).thenReturn(scheduler).","In Kotlin, declare factory lambdas as () -> Scheduler (non-null return type) so 'return null' does not compile."],"tags":["rxandroid","rxjava","null-pointer","plugins","scheduler","lazy-init"],"backgroundTag":null,"analyzedSha":"afaea280461456b30e6ea608428517476ddb8b39","analyzedAt":"2026-08-14T12:58:42.270Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}