apache/seatunnel · error · IllegalArgumentException
intervalMs must be positive, got: %s
Error message
intervalMs must be positive, got: %s
What it means
registerTimerFlushTask validates that the timer flush interval (intervalMs) is strictly positive before scheduling the periodic flush task. A non-positive interval would create a useless or hot-looping scheduled task, so the constructor-level guard rejects it immediately with IllegalArgumentException.
Source
Thrown at seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/TaskExecutionService.java:1011
}
/**
* Register or replace a periodic timer-flush task for one source subtask.
*
* <p>If a timer already exists for the same {@link TaskLocation}, cancel it first. The task is
* scheduled with fixed delay on {@code timerFlushWorker} and stored in {@code
* timerFlushFutures}.
*
* @param taskLocation source subtask location (map key)
* @param callback flush callback to run on each tick
* @param intervalMs flush interval in milliseconds, must be > 0
* @return scheduled future for later cancellation
* @throws IllegalArgumentException if intervalMs <= 0
*/
public ScheduledFuture<?> registerTimerFlushTask(
TaskLocation taskLocation, Runnable callback, long intervalMs) {
if (intervalMs <= 0) {
throw new IllegalArgumentException("intervalMs must be positive, got: " + intervalMs);
}
TaskGroupLocation groupLocation = taskLocation.getTaskGroupLocation();
ConcurrentMap<TaskLocation, ScheduledFuture<?>> groupFutures =
timerFlushFutures.computeIfAbsent(groupLocation, k -> new ConcurrentHashMap<>());
ScheduledFuture<?> existing = groupFutures.remove(taskLocation);
if (existing != null && !existing.isDone()) {
existing.cancel(false);
}
MDCScheduledExecutorService mdcTimerFlushWorker = MDCTracer.tracing(timerFlushWorker);
Runnable namedCallback = new NamedTaskWrapper(callback, "TimerFlush-" + taskLocation);
ScheduledFuture<?> future =
mdcTimerFlushWorker.scheduleWithFixedDelay(
namedCallback, intervalMs, intervalMs, TimeUnit.MILLISECONDS);
groupFutures.put(taskLocation, future);
logger.info(
String.format(View on GitHub (pinned to cf67b549a7)
Solutions
- Pass a strictly positive intervalMs, e.g. at least 1 ms; use a distinct flag to disable flushing instead of 0
- Check the metrics/timer flush interval config option value and its default — fix the config or the default
- Validate/clamp the interval at config-parse time (e.g. require >= 1000ms) so the error surfaces early with the config key
- If converting units, verify the multiplier (e.g. seconds * 1000) so a 0 second value doesn't silently become 0 ms
Example fix
// before
long intervalMs = 0; // intended: disabled
taskExecutionService.registerTimerFlushTask(taskLocation, callback, intervalMs);
// after
long intervalMs = metricsFlushIntervalMs; // from config
if (intervalMs > 0) {
taskExecutionService.registerTimerFlushTask(taskLocation, callback, intervalMs);
} Defensive patterns
Strategy: validation
Validate before calling
if (intervalMs == null || intervalMs <= 0) {
throw new IllegalArgumentException("intervalMs must be > 0, got: " + intervalMs);
} Type guard
boolean isValidInterval(Long ms) { return ms != null && ms > 0; } Prevention
- Never use 0 to mean 'disabled' — use a boolean flag or omit the call
- Validate interval config at parse time with a documented minimum
- Double-check unit conversions (seconds -> milliseconds)
When it happens
Trigger: Calling TaskExecutionService.registerTimerFlushTask(taskLocation, callback, intervalMs) with intervalMs <= 0 — typically from a metrics/timer flush configuration resolved to 0, a negative value, or an uninitialized long default.
Common situations: Metrics flush interval set to 0 in job config believing 0 means 'disabled'; a config option typed as long whose default was never set; unit conversion bugs (seconds vs milliseconds) producing 0.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- ${CONNECTOR_JAR_HA_STORAGE_TYPE} must in [localfile, hdfs]
- The Jar package file for the connector is empty!
- The physical plan didn't have any can execute pipeline
- Invalid jobId for trace file path: ${jobId}
- Parameter 'pluginName' cannot be empty.
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/f48d2434fa2a01e9.
Report an issue: GitHub.