{"record":{"id":"1936fc22d6dad8de","repo":"ReactiveX/RxAndroid","slug":"run-null","errorCode":null,"errorMessage":"run == null","messagePattern":"run == null","errorType":"exception","errorClass":"NullPointerException","httpStatus":null,"severity":"error","filePath":"rxandroid/src/main/java/io/reactivex/rxjava3/android/schedulers/HandlerScheduler.java","lineNumber":36,"sourceCode":"import android.os.Message;\nimport io.reactivex.rxjava3.core.Scheduler;\nimport io.reactivex.rxjava3.disposables.Disposable;\nimport io.reactivex.rxjava3.plugins.RxJavaPlugins;\nimport java.util.concurrent.TimeUnit;\n\nfinal class HandlerScheduler extends Scheduler {\n    private final Handler handler;\n    private final boolean async;\n\n    HandlerScheduler(Handler handler, boolean async) {\n        this.handler = handler;\n        this.async = async;\n    }\n\n    @Override\n    @SuppressLint(\"NewApi\") // Async will only be true when the API is available to call.\n    public Disposable scheduleDirect(Runnable run, long delay, TimeUnit unit) {\n        if (run == null) throw new NullPointerException(\"run == null\");\n        if (unit == null) throw new NullPointerException(\"unit == null\");\n\n        run = RxJavaPlugins.onSchedule(run);\n        ScheduledRunnable scheduled = new ScheduledRunnable(handler, run);\n        Message message = Message.obtain(handler, scheduled);\n        if (async) {\n            message.setAsynchronous(true);\n        }\n        handler.sendMessageDelayed(message, unit.toMillis(delay));\n        return scheduled;\n    }\n\n    @Override\n    public Worker createWorker() {\n        return new HandlerWorker(handler, async);\n    }\n\n    private static final class HandlerWorker extends Worker {","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/ReactiveX/RxAndroid/blob/afaea280461456b30e6ea608428517476ddb8b39/rxandroid/src/main/java/io/reactivex/rxjava3/android/schedulers/HandlerScheduler.java#L18-L54","documentation":"HandlerScheduler.scheduleDirect(Runnable, long, TimeUnit) throws this NullPointerException when the run argument is null (a null unit gets its own check on the next line). HandlerScheduler is what AndroidSchedulers.from(looper) and AndroidSchedulers.main() return, so this is the direct-scheduling entry point of the Android schedulers. Since RxJava operators never pass null runnables, this NPE comes from calling the scheduler API directly with null.","triggerScenarios":"Calling AndroidSchedulers.main().scheduleDirect(null) or scheduleDirect(null, 500, TimeUnit.MILLISECONDS); a Kotlin lambda variable of type Runnable? forwarded to the scheduler; wrapping the scheduler in a delegating class that passes an unchecked parameter; a mocked or generated Runnable that is null (e.g. Mockito stub returning null).","commonSituations":"Custom schedulers/decorators around AndroidSchedulers that lose the runnable in transit; Kotlin code where a nullable function reference is passed as Runnable; test doubles whose schedule methods receive unstubbed null arguments; copy-pasted scheduling utility code with an uninitialized field used as the runnable.","solutions":["Pass a real Runnable: scheduleDirect(() -> doWork(), 0, TimeUnit.MILLISECONDS).","Null-check before scheduling and fail with your own message: Objects.requireNonNull(run, \"task\").","In Kotlin, hold the task as () -> Unit (non-null) and pass it as Runnable; the type system then rejects null.","If the null comes from a wrapper/decorator, fix the decorator to validate and forward, not silently pass null."],"exampleFix":"// before\nRunnable task = maybeTask; // null in some path\nAndroidSchedulers.main().scheduleDirect(task, 200, TimeUnit.MILLISECONDS); // NPE: run == null\n\n// after\nObjects.requireNonNull(maybeTask, \"task\").run();\nAndroidSchedulers.main().scheduleDirect(maybeTask, 200, TimeUnit.MILLISECONDS);","handlingStrategy":"validation","validationCode":"if (run == null || unit == null) {\n    throw new IllegalArgumentException(\"run and unit are required\");\n}\nAndroidSchedulers.main().scheduleDirect(run, delay, unit);","typeGuard":"public static boolean isSchedulable(Runnable r) {\n    return r != null;\n}","tryCatchPattern":null,"preventionTips":["Construct the Runnable fully before scheduling; never pass a field that may be uninitialized.","In Kotlin, use non-null () -> Unit / Runnable types so null cannot reach the scheduler.","Prefer observable operators (Observable.timer, observeOn) over manual scheduleDirect calls — they never pass null."],"tags":["rxandroid","rxjava","null-pointer","scheduler","runnable"],"backgroundTag":null,"analyzedSha":"afaea280461456b30e6ea608428517476ddb8b39","analyzedAt":"2026-08-14T12:58:42.270Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}