apache/iceberg · error · IllegalArgumentException

Cannot delete map keys:

Error message

Cannot delete map keys: 

What it means

Iceberg map types store keys as a hidden nested field, and the schema evolution spec forbids any change to map keys because keys are part of the data's identity/partitioning semantics. When SchemaUpdate.apply detects that the map's key field ID appears in the deletes set, it throws this IllegalArgumentException rather than producing an invalid updated schema.

Source

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

          elementUpdate != null ? elementUpdate.isOptional() : list.isElementOptional();

      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();

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Do not delete map keys; instead delete the whole map column by its top-level field ID
  2. Restrict delete operations to fields surfaced as user-manageable (top-level columns and struct fields)

Example fix

// before
updateSchema.delete(mapType.fields().get(0).fieldId()); // key id — throws
// after
updateSchema.delete(mapColumn.fieldId()); // delete the map column instead
Defensive patterns

Strategy: validation

Validate before calling

Types.MapType map = ...;
int keyId = map.fields().get(0).fieldId();
if (requestedDeletes.contains(keyId)) throw new IllegalArgumentException("Map keys cannot be deleted; delete the map column " + mapColumnId + " instead");

Type guard

static boolean isMapKeyId(Types.NestedField mapColumn) {
  return mapColumn.type() instanceof Types.MapType && mapColumn.type().asMapType().fields().get(0).fieldId() != mapColumn.fieldId();
}
// guard: requestedDeleteId != mapType.asMapType().keyId()

Try / catch

try {
  updateSchema.apply();
} catch (IllegalArgumentException e) {
  if (e.getMessage().equals("Cannot delete map keys: " + e.getMessage().substring("Cannot delete map keys: ".length()))) { /* always rebuild message check */ }
  if (e.getMessage().startsWith("Cannot delete map keys")) throw new IllegalStateException("Drop and re-add the map column instead", e);
  throw e;
}

Prevention

When it happens

Trigger: Calling updateSchema.delete(keyFieldId) where keyFieldId is the internal field ID of a map's key (map.fields().get(0).fieldId()).

Common situations: Generic schema-cleanup tooling iterating over all nested field IDs (including implicit map key/value IDs) and deleting them; users misreading the map key's field ID from Schema.description/ids and targeting it for removal.

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/ee4f81e0852cd6cf. Report an issue: GitHub.