apache/seatunnel · error · IllegalArgumentException

oracle_insert_mode=APPEND_VALUES only supports insert-only w

Error message

oracle_insert_mode=APPEND_VALUES only supports insert-only writes without primary keys.

What it means

Thrown by JdbcOutputFormatBuilder.validateOracleInsertMode when oracle_insert_mode=APPEND_VALUES is configured on an Oracle sink but primary keys are present. APPEND_VALUES is only valid for insert-only writes; primary-key presence implies upsert/merge semantics which cannot use the hint.

Source

Thrown at seatunnel-connectors-v2/connector-jdbc/src/main/java/org/apache/seatunnel/connectors/seatunnel/jdbc/internal/JdbcOutputFormatBuilder.java:384

                    "oracle_insert_mode=APPEND_VALUES only supports generated INSERT statements.");
        }
        String appendValuesSQL =
                insertPrefix + " /*+ APPEND_VALUES */" + insertSQL.substring(insertPrefix.length());
        log.info("Oracle APPEND_VALUES insert mode is enabled, generated SQL: {}", appendValuesSQL);
        return appendValuesSQL;
    }

    private static void validateOracleInsertMode(
            JdbcDialect dialect, JdbcSinkConfig jdbcSinkConfig, List<String> primaryKeys) {
        if (!isOracleAppendValuesConfigured(jdbcSinkConfig)) {
            return;
        }
        if (!DatabaseIdentifier.ORACLE.equals(dialect.dialectName())) {
            throw new IllegalArgumentException(
                    "oracle_insert_mode=APPEND_VALUES only supports Oracle JDBC sink.");
        }
        if (primaryKeys != null && !primaryKeys.isEmpty()) {
            throw new IllegalArgumentException(
                    "oracle_insert_mode=APPEND_VALUES only supports insert-only writes without primary keys.");
        }
    }

    private static boolean isOracleAppendValuesConfigured(JdbcSinkConfig jdbcSinkConfig) {
        return JdbcSinkConfig.OracleInsertMode.APPEND_VALUES.equals(
                jdbcSinkConfig.getOracleInsertMode());
    }

    static Function<SeaTunnelRow, SeaTunnelRow> createKeyExtractor(int[] pkFields) {
        return row -> {
            Object[] fields = new Object[pkFields.length];
            for (int i = 0; i < pkFields.length; i++) {
                fields[i] = row.getField(pkFields[i]);
            }
            SeaTunnelRow newRow = new SeaTunnelRow(fields);
            newRow.setTableId(row.getTableId());
            return newRow;

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Remove or set oracle_insert_mode back to INSERT if the target table has primary keys
  2. Drop the primary_keys entry in the sink config if writes are truly append-only
  3. Use Oracle's native MERGE/upsert path instead of APPEND_VALUES for keyed writes

Example fix

// before
primary_keys = ["id"]
oracle_insert_mode = "APPEND_VALUES"
// after
oracle_insert_mode = "INSERT"
Defensive patterns

Strategy: validation

Validate before calling

// APPEND_VALUES requires no primary keys
if ("APPEND_VALUES".equals(config.get("oracle_insert_mode")) && primaryKeys != null && !primaryKeys.isEmpty()) {
  throw new IllegalArgumentException("APPEND_VALUES cannot be used with primary keys");
}

Try / catch

try { builder.build(); } catch (IllegalArgumentException e) {
  if (e.getMessage().contains("insert-only")) { /* switch to default insert mode */ }
}

Prevention

When it happens

Trigger: Config sets oracle_insert_mode=APPEND_VALUES and the sink resolves a non-empty primary key list (from schema or config).

Common situations: Table has a primary key and SeaTunnel auto-detects it; user expects APPEND_VALUES to work with keyed writes; copying configs between keyed and non-keyed tables.

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


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