apache/beam · error · IllegalArgumentException

Write Statement and Table are mutually exclusive configurati

Error message

Write Statement and Table are mutually exclusive configurations

What it means

JdbcWriteSchemaTransformConfiguration supports writing either a custom write statement OR a table (given via location), but not both. Supplying both is ambiguous, so validate() throws this IllegalArgumentException.

Source

Thrown at sdks/java/io/jdbc/src/main/java/org/apache/beam/sdk/io/jdbc/JdbcWriteSchemaTransformProvider.java:416

        throw new IllegalArgumentException(
            "If JDBC type is not specified, then Driver Class Name and Driver Jars must be specified.");
      }
      if (!driverClassNamePresent && !jdbcTypePresent) {
        throw new IllegalArgumentException(
            "One of JDBC Driver class name or JDBC type must be specified.");
      }
      if (jdbcTypePresent
          && !JDBC_DRIVER_MAP.containsKey(Objects.requireNonNull(jdbcType).toLowerCase())) {
        throw new IllegalArgumentException(
            "JDBC type must be one of " + JDBC_DRIVER_MAP.keySet() + " but was " + jdbcType);
      }

      boolean writeStatementPresent =
          (getWriteStatement() != null && !"".equals(getWriteStatement()));
      boolean locationPresent = (getLocation() != null && !"".equals(getLocation()));

      if (writeStatementPresent && locationPresent) {
        throw new IllegalArgumentException(
            "Write Statement and Table are mutually exclusive configurations");
      }
      if (!writeStatementPresent && !locationPresent) {
        throw new IllegalArgumentException("Either Write Statement or Table must be set.");
      }
    }

    public static Builder builder() {
      return new AutoValue_JdbcWriteSchemaTransformProvider_JdbcWriteSchemaTransformConfiguration
          .Builder();
    }

    public abstract Builder toBuilder();

    @AutoValue.Builder
    public abstract static class Builder {
      public abstract Builder setDriverClassName(String value);

View on GitHub (pinned to 12126d8942)

Solutions

  1. Remove setWriteStatement(...) and keep setLocation(table) for a plain table insert
  2. Or remove setLocation(...) and keep the custom write statement
  3. Decide the write mode in code: if statement != null, skip setting location

Example fix

// before
builder().setJdbcUrl(url).setLocation("orders").setWriteStatement("INSERT INTO orders VALUES(?)").build();
// after
builder().setJdbcUrl(url).setWriteStatement("INSERT INTO orders VALUES(?)").build();
Defensive patterns

Strategy: validation

Validate before calling

if (cfg.getWriteStatement() != null && !cfg.getWriteStatement().isEmpty()
    && cfg.getLocation() != null && !cfg.getLocation().isEmpty()) {
  throw new IllegalArgumentException("Set either writeStatement or location, not both");
}

Try / catch

try {
  config.validate();
} catch (IllegalArgumentException e) {
  if (e.getMessage().contains("mutually exclusive")) {
    LOG.error("Drop either writeStatement or location from the config");
  }
  throw e;
}

Prevention

When it happens

Trigger: Setting both setWriteStatement(...) and setLocation(...) on the configuration before validate().

Common situations: Merging configs where a template already contains a write statement and the user also sets the target table; scripted config assembly appending both fields unconditionally.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/5cd3ad44a8d668d8. Report an issue: GitHub.