apache/seatunnel · error · OptionValidationException

tables_configs[ ]: 'topics' must not be empty

Error message

tables_configs[%d]: 'topics' must not be empty

What it means

RocketMqSourceFactory.validate validates each entry of `tables_configs` at option-rule evaluation time. Each table entry must define a non-empty `topics` option; otherwise OptionValidationException is thrown with the entry index. This fails fast before any job resources are allocated.

Solutions

  1. Add a non-empty `topics` value (comma-separated topic list) to every entry in tables_configs
  2. Check YAML indentation so `topics` actually lands inside the intended table entry
  3. Verify no entry has only whitespace, e.g. topics: " " which also fails validation

Example fix

// before
tables_configs = [
  { topic = "" }
]
// after
tables_configs = [
  { topics = "order-events" }
]
Defensive patterns

Strategy: validation

Validate before calling

config.getTablesConfigs().forEach(tc -> {
    String topics = tc.get("topics");
    if (topics == null || topics.trim().isEmpty())
        throw new IllegalArgumentException("every tables_configs entry needs non-empty 'topics'");
});

Prevention

When it happens

Trigger: A `tables_configs` entry where `topics` is absent, null, empty string, or whitespace-only, detected in evaluate() while parsing entry i.

Common situations: Multi-table configs where one entry was pasted incompletely, YAML indentation errors leaving `topics` unset for one table, or `topics: ""` left as a placeholder.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at seatunnel-connectors-v2/connector-rocketmq/src/main/java/org/apache/seatunnel/connectors/seatunnel/rocketmq/source/RocketMqSourceFactory.java:132

        @Override
        public String description() {
            return "each tables_configs entry must have valid topics, "
                    + "start.mode.timestamp (>= 0) when CONSUME_FROM_TIMESTAMP, "
                    + "and non-empty start.mode.offsets when CONSUME_FROM_SPECIFIC_OFFSETS";
        }

        @Override
        public boolean evaluate(ReadonlyConfig config, List<Map<String, Object>> entries)
                throws OptionValidationException {
            if (entries == null || entries.isEmpty()) {
                return true;
            }
            for (int i = 0; i < entries.size(); i++) {
                ReadonlyConfig tableConfig = ReadonlyConfig.fromMap(entries.get(i));
                String topics = tableConfig.get(RocketMqSourceOptions.TOPICS);
                if (topics == null || topics.trim().isEmpty()) {
                    throw new OptionValidationException(
                            "tables_configs[%d]: 'topics' must not be empty", i);
                }
                StartMode startMode =
                        tableConfig.getOptional(RocketMqSourceOptions.START_MODE).orElse(null);
                if (startMode == StartMode.CONSUME_FROM_TIMESTAMP) {
                    Long ts =
                            tableConfig
                                    .getOptional(RocketMqSourceOptions.START_MODE_TIMESTAMP)
                                    .orElse(null);
                    if (ts == null) {
                        throw new OptionValidationException(
                                "tables_configs[%d]: 'start.mode.timestamp' required "
                                        + "when start.mode=CONSUME_FROM_TIMESTAMP",
                                i);
                    }
                    if (ts < 0) {
                        throw new OptionValidationException(
                                "tables_configs[%d]: 'start.mode.timestamp' must be >= 0, got: %d",

View on GitHub (pinned to cf67b549a7)