apache/seatunnel · error · IllegalArgumentException

'${option.key()}' must be greater than or equal to 0.

Error message

'${option.key()}' must be greater than or equal to 0.

What it means

getNonNegativeSkipValue validates startup.specific-offset.skip-cdcs and startup.specific-offset.skip-events (skip rows/events) which must be >= 0. A negative configured value throws IllegalArgumentException naming the offending option key.

Source

Thrown at seatunnel-connectors-v2/connector-cdc/connector-cdc-mysql/src/main/java/org/apache/seatunnel/connectors/seatunnel/cdc/mysql/source/MySqlIncrementalSource.java:184

        }

        try {
            new GtidSet(gtidSet);
        } catch (Exception e) {
            throw new IllegalArgumentException(
                    String.format(
                            "Invalid '%s' value '%s'.",
                            MySqlIncrementalSourceOptions.STARTUP_SPECIFIC_OFFSET_GTID_SET.key(),
                            gtidSet),
                    e);
        }
        return Optional.of(gtidSet);
    }

    private static long getNonNegativeSkipValue(ReadonlyConfig config, Option<Long> option) {
        long value = config.getOptional(option).orElse(0L);
        if (value < 0) {
            throw new IllegalArgumentException(
                    String.format("'%s' must be greater than or equal to 0.", option.key()));
        }
        return value;
    }

    private static void validateNoSpecificStartupOffset(
            ReadonlyConfig config, StartupMode startupMode) {
        if (hasSpecificStartupOffset(config)) {
            throw new IllegalArgumentException(
                    String.format(
                            "'startup.specific-offset.*' options can only be used when '%s' is 'specific', but current mode is '%s'.",
                            SourceOptions.STARTUP_MODE_KEY, startupMode));
        }
    }

    private static boolean hasSpecificStartupOffset(ReadonlyConfig config) {
        return config.getOptional(SourceOptions.STARTUP_SPECIFIC_OFFSET_FILE).isPresent()
                || config.getOptional(SourceOptions.STARTUP_SPECIFIC_OFFSET_POS).isPresent()

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Set the skip option to 0 or a positive number
  2. Remove the negative key to use the default of 0
  3. If the goal is to start earlier, choose an earlier binlog file/position or GTID set instead of negative skip

Example fix

// before
specific-offset.skip-events = -100
// after
specific-offset.skip-events = 0
Defensive patterns

Strategy: validation

Validate before calling

long skip = config.getOptional("startup.specific-offset.skip-events").orElse(0L);
if (skip < 0) throw new IllegalArgumentException("skip-events must be >= 0");

Try / catch

try { createSource(config); } catch (IllegalArgumentException e) { LOG.error("Negative skip value: {}", e.getMessage()); throw new ConfigException(e); }

Prevention

When it happens

Trigger: createSpecificStartupOffset calling getNonNegativeSkipValue for skip.rows/skip.events with a configured value < 0 (default 0 when absent).

Common situations: User tries to 'rewind' events with a negative skip; sign typo (-1 as placeholder); templated config where a negative default leaked in.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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