apache/pulsar · error · IllegalArgumentException

period can not be null

Error message

period can not be null

What it means

SingleThreadNonConcurrentFixedRateScheduler.scheduleAtFixedRateNonConcurrently validates its inputs: a null command or null unit throws NullPointerException (mirroring ScheduledExecutorService semantics), and a period <= 0 throws this IllegalArgumentException. Despite the message text 'can not be null', the condition is actually a non-positive period — the message is misleading but the trigger is period <= 0.

Source

Thrown at pulsar-common/src/main/java/org/apache/pulsar/common/util/SingleThreadNonConcurrentFixedRateScheduler.java:118

    @Override
    public Future<?> submit(Runnable task) {
        return super.submit(new SafeRunnable(task));
    }

    /***
     * Different with {@link #scheduleAtFixedRate(Runnable, long, long, TimeUnit)}, If the execution time of the next
     * period task > period: New tasks will trigger be dropped, instead, execute the next period task after the current
     * time.
     */
    public ScheduledFuture<?> scheduleAtFixedRateNonConcurrently(Runnable command,
                                                                 long initialDelay,
                                                                 long period,
                                                                 TimeUnit unit) {
        if (command == null || unit == null) {
            throw new NullPointerException();
        }
        if (period <= 0L) {
            throw new IllegalArgumentException("period can not be null");
        }
        ScheduledFutureTask<Void> sft =
                new ScheduledFutureTask<Void>(command,
                        null,
                        triggerTime(initialDelay, unit),
                        unit.toNanos(period),
                        fixRateTaskSequencerGenerator.getAndIncrement());
        RunnableScheduledFuture<Void> t = decorateTask(command, sft);
        sft.outerTask = t;
        delayedExecute(t);
        return t;
    }

    /**
     * Main execution method for delayed or periodic tasks.  If pool
     * is shut down, rejects the task. Otherwise adds task to queue
     * and starts a thread, if necessary, to run it.  (We cannot
     * prestart the thread to run the task because the task (probably)

View on GitHub (pinned to 820761864e)

Solutions

  1. Pass a strictly positive period (period > 0) to scheduleAtFixedRateNonConcurrently.
  2. Validate/normalize the configured interval before scheduling and clamp or reject zero/negative values.
  3. Check where the period value originates (config parsing, unit conversion) to fix the source of the zero/negative value.
  4. If a run-once semantic was intended, use schedule() with an initialDelay instead of a fixed-rate schedule with period 0.

Example fix

// before
scheduler.scheduleAtFixedRateNonConcurrently(task, 0, periodFromConfig, TimeUnit.SECONDS); // periodFromConfig = 0
// after
if (periodFromConfig <= 0) { periodFromConfig = DEFAULT_PERIOD_SECONDS; }
scheduler.scheduleAtFixedRateNonConcurrently(task, 0, periodFromConfig, TimeUnit.SECONDS);
Defensive patterns

Strategy: validation

Validate before calling

static void validateScheduleArgs(Runnable command, long period, TimeUnit unit) {
    java.util.Objects.requireNonNull(command, "command");
    java.util.Objects.requireNonNull(unit, "unit");
    if (period <= 0L) {
        throw new IllegalArgumentException("period must be > 0, got: " + period);
    }
}

Type guard

static boolean isValidPeriod(long period) { return period > 0L; }

Try / catch

try {
    scheduler.scheduleAtFixedRateNonConcurrently(task, initialDelay, period, TimeUnit.SECONDS);
} catch (IllegalArgumentException e) {
    LOG.error("Invalid schedule period {} for task {}", period, task, e);
} catch (NullPointerException e) {
    LOG.error("Null command or unit passed to scheduler", e);
}

Prevention

When it happens

Trigger: Calling scheduleAtFixedRateNonConcurrently(command, initialDelay, period, unit) with period <= 0 (e.g. 0 or negative values computed from configuration, integer underflow, or a misparsed interval of '0').

Common situations: Configuration value for task interval is 0 or negative because a config key defaulted to 0, a properties file had 'period=0', or arithmetic (e.g. maxInterval - minInterval) produced 0; also seen when porting code that previously relied on ScheduledThreadPoolExecutor throwing NPE/IAE ordering differently.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/5770ef92440bbb86. Report an issue: GitHub.