apache/iceberg · error · IllegalArgumentException

Cannot update map keys:

Error message

Cannot update map keys: 

What it means

Map keys in Iceberg are immutable identity components of the map type; the schema evolution rules prohibit updating (renaming, retyping, re-required-ness) the hidden key field. If SchemaUpdate.apply finds the map key's field ID in the updates map, it throws this IllegalArgumentException.

Source

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

      if (isElementOptional == elementField.isOptional() && list.elementType() == elementType) {
        return list;
      }

      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) {

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Do not update map key fields; rebuild the map column with a new key type by dropping and re-adding the column (acknowledging data rewrite semantics)
  2. Restrict updateColumn/require calls to non-key field IDs

Example fix

// before
updateSchema.updateColumn(mapKeyFieldId, Types.LongType.get()); // throws
// after
updateSchema.delete(mapColumn.fieldId());
updateSchema.addColumn(parent, name, Types.MapType.ofOptional(keyId, valueId, Types.LongType.get(), valueType));
Defensive patterns

Strategy: validation

Validate before calling

Types.MapType map = ...;
int keyId = map.fields().get(0).fieldId();
if (requestedUpdates.containsKey(keyId)) throw new IllegalArgumentException("Map keys cannot be updated; rebuild the map column instead");

Type guard

static boolean updateTouchesMapKey(UpdateSchema update, Types.MapType map) {
  int keyId = map.fields().get(0).fieldId();
  return update.appliedUpdates().stream().anyMatch(u -> u instanceof UpdateColumn && ((UpdateColumn) u).fieldId() == keyId);
}

Try / catch

try {
  updateSchema.apply();
} catch (IllegalArgumentException e) {
  if (e.getMessage().startsWith("Cannot update map keys")) throw new IllegalStateException("Drop and re-add the map with the desired key type (requires data rewrite)", e);
  throw e;
}

Prevention

When it happens

Trigger: Calling updateSchema.updateColumn(keyFieldId, ...) / requireOptional(keyFieldId) / rename(keyFieldId, ...) where the target ID is a map key's internal field ID.

Common situations: Bulk update scripts applying type-widening or optionality changes to every nested field ID, accidentally including map key IDs; attempts to 'widen' key types via generic update tooling.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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