apache/iceberg · error · RuntimeException

Unrecognized ${WRITE_DISTRIBUTION_MODE}: ${writeMode}

Error message

Unrecognized ${WRITE_DISTRIBUTION_MODE}: ${writeMode}

What it means

FlinkSink.distributeDataStream switches on the resolved DistributionMode for the write; if the mode is not NONE, HASH, or RANGE it throws a RuntimeException naming the WRITE_DISTRIBUTION_MODE property and the offending value. This is a defensive default-branch guard — the enum is parsed by FlinkWriteConf, so this only fires when a DistributionMode value escapes validation.

Source

Thrown at flink/v1.20/flink/src/main/java/org/apache/iceberg/flink/sink/FlinkSink.java:719

            shuffleStream = shuffleStream.uid(uidPrefix + "-shuffle");
          }

          return shuffleStream
              .partitionCustom(new RangePartitioner(iSchema, sortOrder), r -> r)
              .flatMap(
                  (FlatMapFunction<StatisticsOrRecord, RowData>)
                      (statisticsOrRecord, out) -> {
                        if (statisticsOrRecord.hasRecord()) {
                          out.collect(statisticsOrRecord.record());
                        }
                      })
              // Set the parallelism same as writerParallelism to
              // promote operator chaining with the downstream writer operator
              .setParallelism(writerParallelism)
              .returns(RowData.class);

        default:
          throw new RuntimeException("Unrecognized " + WRITE_DISTRIBUTION_MODE + ": " + writeMode);
      }
    }
  }

  /**
   * Clean up after removing {@link Builder#tableSchema}
   *
   * @deprecated since 1.10.0, will be removed in 2.0.0. Use {@link #toFlinkRowType(Schema,
   *     ResolvedSchema)} instead.
   */
  @Deprecated
  static RowType toFlinkRowType(Schema schema, TableSchema requestedSchema) {
    if (requestedSchema != null) {
      // Convert the flink schema to iceberg schema using the table schema as the reference.
      Schema writeSchema = FlinkSchemaUtil.convert(schema, requestedSchema);
      TypeUtil.validateWriteSchema(schema, writeSchema, true, true);

      // We use this flink schema to read values from RowData. The flink's TINYINT and SMALLINT will

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Check for duplicate/mixed iceberg-flink-runtime versions on the classpath (mvn dependency:tree, shade config) and pin a single version
  2. Align the Iceberg runtime jar version used by the cluster and the job (e.g. upgrade iceberg-flink-runtime to match your build)
  3. Set write-distribution-mode explicitly to none|hash|range so a known-good value is used
  4. If nothing changed, inspect the log line 'Write distribution mode is ...' to see the resolved mode and trace where it came from

Example fix

// before
builder.set("write-distribution-mode", "batch-shuffle"); // unrecognized
// after
builder.set("write-distribution-mode", DistributionMode.HASH.modeName()); // "hash"
Defensive patterns

Strategy: validation

Validate before calling

String mode = writeOptions.getOrDefault("write-distribution-mode", "none");
if (!Set.of("none", "hash", "range").contains(mode.toLowerCase(Locale.ROOT))) {
  throw new IllegalArgumentException("unsupported write-distribution-mode: " + mode);
}

Try / catch

try {
  sinkBuilder.append();
} catch (RuntimeException e) {
  if (e.getMessage() != null && e.getMessage().contains("Unrecognized")) {
    LOG.error("distribution mode rejected: {}", e.getMessage());
    // fall back to explicit hash mode
  }
  throw e;
}

Prevention

When it happens

Trigger: Builder#distributionMode / write option 'write-distribution-mode' / FlinkWriteOptions.DISTRIBUTION_MODE resolves to a DistributionMode constant outside the three handled cases (no current user-facing value can); effectively only reachable via a newer enum constant running against older sink code, or custom DistributionMode injection.

Common situations: Version mismatch: a Flink job or SQL planner compiled against a newer Iceberg that added an enum value is run with an older iceberg-flink-runtime jar on the classpath; or fat-jar shading mixes two Iceberg versions so the parsed enum differs from the one the sink switch handles.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/c903b049ca7c8054. Report an issue: GitHub.