apache/seatunnel · error · IllegalArgumentException

start_timestamp must be less than end_timestamp

Error message

start_timestamp must be less than end_timestamp

What it means

applyTimeRange enforces that the resolved time range is non-empty: after defaulting start to 0 and end to Long.MAX_VALUE, min >= max throws IllegalArgumentException('start_timestamp must be less than end_timestamp'). An empty or inverted range cannot be passed to Scan.setTimeRange, so the library fails fast.

Source

Thrown at seatunnel-connectors-v2/connector-hbase/src/main/java/org/apache/seatunnel/connectors/seatunnel/hbase/client/HbaseClient.java:413

    private static void applyTimeRange(Scan scan, HbaseParameters hbaseParameters)
            throws IOException {
        Long startTimestamp = hbaseParameters.getStartTimestamp();
        Long endTimestamp = hbaseParameters.getEndTimestamp();
        if (startTimestamp == null && endTimestamp == null) {
            return;
        }

        if (startTimestamp != null && startTimestamp < 0) {
            throw new IllegalArgumentException("start_timestamp can't be negative");
        }
        if (endTimestamp != null && endTimestamp < 0) {
            throw new IllegalArgumentException("end_timestamp can't be negative");
        }

        long min = startTimestamp == null ? 0L : startTimestamp;
        long max = endTimestamp == null ? Long.MAX_VALUE : endTimestamp;
        if (min >= max) {
            throw new IllegalArgumentException("start_timestamp must be less than end_timestamp");
        }
        scan.setTimeRange(min, max);
    }

    /**
     * Get a RegionLocator.
     *
     * @param tableName table name (preferably fully qualified as {@code namespace:table})
     * @return RegionLocator
     * @throws IOException exception
     * @deprecated Use {@link #getRegionLocator(String, String)} instead to avoid relying on the
     *     default namespace behavior.
     */
    @Deprecated
    public RegionLocator getRegionLocator(String tableName) throws IOException {
        return this.connection.getRegionLocator(TableName.valueOf(tableName));
    }

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Ensure start_timestamp < end_timestamp in the job config
  2. If you need an unbounded range, omit one or both options instead of setting equal values
  3. Fix any generation logic that can emit start == end (e.g. zero-length windows)

Example fix

// before
start_timestamp = 1700003600000
end_timestamp = 1700003600000
// after
start_timestamp = 1700000000000
end_timestamp = 1700003600000
Defensive patterns

Strategy: validation

Validate before calling

if (start != null && end != null && start >= end) {
    throw new IllegalArgumentException("start_timestamp must be < end_timestamp");
}

Type guard

static boolean validTimeRange(Long start, Long end) {
    long s = start == null ? 0L : start;
    long e = end == null ? Long.MAX_VALUE : end;
    return s < e;
}

Try / catch

try { client.buildScan(params); }
catch (IllegalArgumentException e) {
    if (e.getMessage().contains("must be less than")) {
        // swap or correct start/end values in config
    } else throw e;
}

Prevention

When it happens

Trigger: Setting start_timestamp >= end_timestamp (including both equal) in the connector options, so buildScan's applyTimeRange computes min >= max.

Common situations: Copy-pasting the same timestamp for both bounds; swapped start/end values; window computation producing a zero-width range.

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


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/9377619ed01a6377. Report an issue: GitHub.