apache/iceberg · error · ConfigException
${msg}
Error message
${msg} What it means
IcebergSinkConfig.checkState is a helper that converts a failed boolean condition during configuration validation into a ConfigException with the given message. The thrown message '${msg}' is whatever validation text was passed in — e.g. 'Cannot specify both static and dynamic table names' or 'Must specify a route field if using dynamic table names'.
Source
Thrown at kafka-connect/kafka-connect/src/main/java/org/apache/iceberg/connect/IcebergSinkConfig.java:300
validate();
}
private void validate() {
checkState(!catalogProps().isEmpty(), "Must specify Iceberg catalog properties");
if (tables() != null) {
checkState(!dynamicTablesEnabled(), "Cannot specify both static and dynamic table names");
} else if (dynamicTablesEnabled()) {
checkState(
tablesRouteField() != null, "Must specify a route field if using dynamic table names");
} else {
throw new ConfigException("Must specify table name(s)");
}
}
private void checkState(boolean condition, String msg) {
if (!condition) {
throw new ConfigException(msg);
}
}
public String connectorName() {
return originalProps.get(NAME_PROP);
}
public String taskId() {
return originalProps.get(TASK_ID);
}
public String transactionalSuffix() {
// this is for internal use and is not part of the config definition...
return originalProps.get(INTERNAL_TRANSACTIONAL_SUFFIX_PROP);
}
public Map<String, String> catalogProps() {
return catalogProps;View on GitHub (pinned to 86d9c8fc54)
Solutions
- If the message says both were specified, remove 'iceberg.tables' or set 'iceberg.tables.dynamic-enabled=false'.
- If the message mentions a route field, set 'iceberg.tables.route-field' to the record field that determines the target table.
- Read the ConfigException message itself — it names the exact violated rule.
Example fix
// before "iceberg.tables": "db.t1", "iceberg.tables.dynamic-enabled": "true" // after "iceberg.tables.dynamic-enabled": "true", "iceberg.tables.route-field": "targetTable"
Defensive patterns
Strategy: validation
Validate before calling
// Reject mutually exclusive / incomplete dynamic routing config up front
boolean dyn = Boolean.parseBoolean(config.getOrDefault("iceberg.tables.dynamic-enabled", "false"));
if (dyn && config.get("iceberg.tables") != null) throw new IllegalArgumentException("remove iceberg.tables when dynamic routing is enabled");
if (dyn && config.get("iceberg.tables.route-field") == null) throw new IllegalArgumentException("iceberg.tables.route-field is required for dynamic routing"); Try / catch
try { new IcebergSinkConfig(props); } catch (org.apache.kafka.common.config.ConfigException e) { log.error("Config rule violated: {}", e.getMessage()); } Prevention
- Don't mix static 'iceberg.tables' with dynamic routing
- Always pair dynamic-enabled=true with tables.route-field
- Test config changes in staging before production connectors
When it happens
Trigger: Called from validate() when: both iceberg.tables and iceberg.tables.dynamic-enabled=true are set, or dynamic tables are enabled without iceberg.tables.route-field.
Common situations: Developers partially migrating between static and dynamic table routing — enabling dynamic mode while leaving static tables configured, or enabling dynamic mode without specifying the record field used for routing.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
Related errors
- Must specify table name(s)
- Invalid rewrite job order name: %s
- Unknown row-level operation mode:
- Unrecognized ${WRITE_DISTRIBUTION_MODE}: ${writeMode}
- Unrecognized ${WRITE_DISTRIBUTION_MODE}: ${mode}
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/a1d38fefd6bd52a8.
Report an issue: GitHub.