apache/iceberg · error · IllegalArgumentException

Cannot alter map keys:

Error message

Cannot alter map keys: 

What it means

After the SchemaUpdate visitor recursively computes the new key type (kResult), it verifies the key type is unchanged: map.keyType().equals(kResult). If any operation altered the key's type despite the earlier guards, this IllegalArgumentException is thrown, enforcing Iceberg's rule that map keys are immutable in schema evolution.

Source

Thrown at core/src/main/java/org/apache/iceberg/SchemaUpdate.java:724

      if (isElementOptional) {
        return Types.ListType.ofOptional(list.elementId(), elementType);
      } else {
        return Types.ListType.ofRequired(list.elementId(), elementType);
      }
    }

    @Override
    public Type map(Types.MapType map, Type kResult, Type valueResult) {
      // if any updates are intended for the key, throw an exception
      int keyId = map.fields().get(0).fieldId();
      if (deletes.contains(keyId)) {
        throw new IllegalArgumentException("Cannot delete map keys: " + map);
      } else if (updates.containsKey(keyId)) {
        throw new IllegalArgumentException("Cannot update map keys: " + map);
      } else if (parentToAddedIds.containsKey(keyId)) {
        throw new IllegalArgumentException("Cannot add fields to map keys: " + map);
      } else if (!map.keyType().equals(kResult)) {
        throw new IllegalArgumentException("Cannot alter map keys: " + map);
      }

      // use field to apply updates to the value
      Types.NestedField valueField = map.fields().get(1);
      Type valueType = field(valueField, valueResult);
      if (valueType == null) {
        throw new IllegalArgumentException("Cannot delete value type from map: " + map);
      }

      Types.NestedField valueUpdate = updates.get(valueField.fieldId());
      boolean isValueOptional =
          valueUpdate != null ? valueUpdate.isOptional() : map.isValueOptional();

      if (isValueOptional == map.isValueOptional() && map.valueType() == valueType) {
        return map;
      }

      if (isValueOptional) {

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Audit the update operations for anything touching the key field's type and remove it
  2. Ensure updates are applied only to value fields and non-key columns
  3. Build a new map column (drop + add) if a key type change is genuinely required

Example fix

// before
// custom visitor that widens all int keys to long via updateColumn
updateSchema.updateColumn(mapValueParentFieldId, ...); // indirectly altering key type → throws
// after
// keep key type fixed; widen only the value
updateSchema.updateColumn(valueFieldId, Types.LongType.get());
Defensive patterns

Strategy: try-catch

Validate before calling

Types.MapType map = ...;
if (!map.keyType().equals(originalKeyType)) throw new IllegalArgumentException("Update must not change map key type " + originalKeyType);

Try / catch

try {
  updateSchema.apply();
} catch (IllegalArgumentException e) {
  if (e.getMessage().startsWith("Cannot alter map keys")) throw new IllegalStateException("Map key type changed during update — review combined operations touching the key field", e);
  throw e;
}

Prevention

When it happens

Trigger: A schema update whose composed operations cause the map key type to change between the original and rebuilt schema (e.g. combined update primitives reaching the key field through a custom TypeUtil visit), detected during SchemaUpdate.apply.

Common situations: Complex multi-operation schema updates assembled programmatically where per-field guards were bypassed; custom UpdateSchema implementations or visitor overrides that transform types beneath the earlier deletes/updates/adds checks.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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