apache/beam · error · IllegalArgumentException

Unsupported logical type in key: %s

Error message

Unsupported logical type in key: %s

What it means

MutationUtils.setBeamValueToKey() converts a Beam Row field into a Spanner key value, dispatching on the logical type identifier (strings, integers, floats, booleans, timestamps, dates, bytes...). A logical type not handled for keys throws IllegalArgumentException('Unsupported logical type in key: %s') because Spanner key columns must be scalar types.

Source

Thrown at sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/spanner/MutationUtils.java:168

      case DOUBLE:
        keyBuilder.append(row.getDouble(columnName));
        break;
      case DECIMAL:
        keyBuilder.append(row.getDecimal(columnName));
        break;
        // TODO: Implement logical date and datetime
      case LOGICAL_TYPE:
        Schema.LogicalType<?, ?> logicalType = checkNotNull(field.getLogicalType());
        String identifier = logicalType.getIdentifier();
        if (identifier.equals(MicrosInstant.IDENTIFIER)) {
          Instant instant = row.getValue(columnName);
          if (instant == null) {
            keyBuilder.append((Timestamp) null);
          } else {
            keyBuilder.append(toSpannerTimestamp(instant));
          }
        } else {
          throw new IllegalArgumentException(
              String.format("Unsupported logical type in key: %s", identifier));
        }
        break;
      case DATETIME:
        @Nullable ReadableDateTime dateTime = row.getDateTime(columnName);
        if (dateTime == null) {
          keyBuilder.append((Timestamp) null);
        } else {
          keyBuilder.append(
              Timestamp.ofTimeMicroseconds(dateTime.toInstant().getMillis() * 1_000L));
        }
        break;
      case BOOLEAN:
        keyBuilder.append(row.getBoolean(columnName));
        break;
      case STRING:
        keyBuilder.append(row.getString(columnName));
        break;

View on GitHub (pinned to 12126d8942)

Solutions

  1. Use only supported scalar key logical types (STRING, INT64, FLOAT64, BOOLEAN, TIMESTAMP, DATE, BYTES, NUMERIC).
  2. Flatten or drop non-scalar columns from the key mapping.
  3. Convert exotic logical types to a supported scalar (e.g. encode as STRING) before constructing the key row.

Example fix

// before: key field is an ARRAY logical type
// after: key field is STRING, e.g. StructField.of("id", FieldType.STRING)
Defensive patterns

Strategy: validation

Validate before calling

for (Field f : keyRow.getSchema().getFields()) {
  if (!Set.of("STRING","INT64","FLOAT64","BOOLEAN","DATETIME","BYTES","logicalType(Numeric)","logicalType(Timestamp)").contains(f.getType().getIdentifier()))
    throw new IllegalArgumentException("key field not supported: " + f.getName());
}

Type guard

boolean isScalarKeyField(Field f) { return !f.getType().getTypeName().isCollectionType() && !f.getType().getTypeName().isCompositeType(); }

Try / catch

try { Key k = MutationUtils.createKeyFromBeamRow(row); } catch (IllegalArgumentException e) { /* flatten key schema */ }

Prevention

When it happens

Trigger: Using a Beam schema field with a logical type like ARRAY, MAP, STRUCT, or a custom logical type as part of the key column mapping when building a delete/update mutation via createKeyFromBeamRow.

Common situations: Beam schema auto-inferred from a source containing non-scalar columns, then used directly as the Spanner key mapping; schema drift after a source table change adds a new column type to the key.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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