apache/iceberg · error · IllegalArgumentException

Cannot delete value type from map:

Error message

Cannot delete value type from map: 

What it means

SchemaUpdate.applyChangesToMap attempts to look up the map's value field when applying a delete requirement to a map type. When the resolved value type is null (the value field does not exist in the resulting schema state), the update throws this IllegalArgumentException instead of silently producing an invalid schema. It protects the invariant that a map always has a key and value field.

Source

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

    @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) {
        return Types.MapType.ofOptional(map.keyId(), map.valueId(), map.keyType(), valueType);
      } else {
        return Types.MapType.ofRequired(map.keyId(), map.valueId(), map.keyType(), valueType);
      }
    }

    @Override

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Remove the delete requirement targeting the map value field ID, or instead require/delete the whole map column
  2. Use updateSchema().deleteColumn on the parent column, not the internal map value field
  3. Verify nested field IDs with table.schema().findField(...) before deleting

Example fix

// before
updateSchema.deleteColumn("events", 21); // 21 = map value field id
// after
updateSchema.deleteColumn("events"); // delete the map column itself
Defensive patterns

Strategy: validation

Validate before calling

Types.NestedField valueField = map.fields().get(1);
if (schema.findField(valueField.fieldId()) == null) {
  throw new IllegalArgumentException("Map value field " + valueField.fieldId() + " does not exist; delete the map column instead");
}

Try / catch

try { update.apply(); } catch (IllegalArgumentException e) { if (e.getMessage().startsWith("Cannot delete value type from map")) { /* delete parent map column instead */ } else { throw e; } }

Prevention

When it happens

Trigger: Calling UpdateSchema.delete on a map's value field ID (or a nested element that resolves to the map value), so that field(valueField, valueResult) returns null and no updates exist to redefine the value.

Common situations: Code that deletes columns by iterating all nested field IDs and accidentally includes map value field IDs; schema evolution scripts targeting the wrong nested ID after spec changes.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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