apache/shenyu · warning · NullPointerException

timer task null

Error message

timer task null

What it means

HierarchicalWheelTimer.add() explicitly throws NullPointerException('timer task null') when passed a null TimerTask. The hierarchical timing wheel cannot schedule an absent task, so the API fails fast before acquiring the read lock and starting the wheel.

Solutions

  1. Ensure the TimerTask is constructed before calling add(); check why the producing code returned null.
  2. Guard the call site with a null check and skip/log instead of scheduling.
  3. Use Objects.requireNonNull earlier in your own code to surface the origin of the null.
  4. If tasks are conditional, build an Optional<TimerTask> and only add when present.

Example fix

// before
TimerTask task = maybeBuildTask();
wheelTimer.add(task); // NPE if null
// after
TimerTask task = maybeBuildTask();
if (task != null) {
    wheelTimer.add(task);
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (task == null) {
    LOG.warn("skipping null timer task");
    return;
}

Type guard

void safeAdd(HierarchicalWheelTimer t, TimerTask task) {
    if (t != null && task != null) {
        t.add(task);
    }
}

Try / catch

try {
    wheelTimer.add(task);
} catch (NullPointerException e) {
    LOG.error("Attempted to schedule null timer task", e);
}

Prevention

When it happens

Trigger: Calling add(timerTask) with a null reference — typically a factory/builder returning null, an optional task computed as null, or a map lookup producing null before scheduling.

Common situations: Conditional task creation where the null branch is passed through unguarded; refactors where getDelayMs-based task wrappers are built lazily and can be null; tests passing null to probe behavior.

Related errors


AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12). Data as JSON: /api/errors/8f049888ca388976. Report an issue: GitHub.

Appendix: source

Thrown at shenyu-common/src/main/java/org/apache/shenyu/common/timer/HierarchicalWheelTimer.java:93

     * @param tickMs       the tick ms
     * @param wheelSize    the wheel size
     * @param startMs      the start ms
     */
    public HierarchicalWheelTimer(final String executorName,
                                  final Long tickMs,
                                  final Integer wheelSize,
                                  final Long startMs) {
        ThreadFactory threadFactory = ShenyuThreadFactory.create(executorName, false);
        taskExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(), threadFactory);
        workerThread = threadFactory.newThread(new Worker(this));
        timingWheel = new TimingWheel(tickMs, wheelSize, startMs, taskCounter, delayQueue);
    }

    @Override
    public void add(final TimerTask timerTask) {
        if (Objects.isNull(timerTask)) {
            throw new NullPointerException("timer task null");
        }
        this.readLock.lock();
        try {
            start();
            long millis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
            this.addTimerTaskEntry(new TimerTaskList.TimerTaskEntry(this, timerTask, timerTask.getDelayMs() + millis));
        } finally {
            this.readLock.unlock();
        }

    }

    private void addTimerTaskEntry(final TimerTaskList.TimerTaskEntry timerTaskEntry) {
        if (!timingWheel.add(timerTaskEntry)) {
            if (!timerTaskEntry.cancelled()) {
                taskExecutor.submit(() -> timerTaskEntry.getTimerTask().run(timerTaskEntry));
            }
        }

View on GitHub (pinned to 567142e072)