{"record":{"id":"8fb27f1fc7ce2fca","repo":"apache/iceberg","slug":"cannot-alter-map-keys","errorCode":null,"errorMessage":"Cannot alter map keys: ","messagePattern":"Cannot alter map keys: ","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/SchemaUpdate.java","lineNumber":724,"sourceCode":"      if (isElementOptional) {\n        return Types.ListType.ofOptional(list.elementId(), elementType);\n      } else {\n        return Types.ListType.ofRequired(list.elementId(), elementType);\n      }\n    }\n\n    @Override\n    public Type map(Types.MapType map, Type kResult, Type valueResult) {\n      // if any updates are intended for the key, throw an exception\n      int keyId = map.fields().get(0).fieldId();\n      if (deletes.contains(keyId)) {\n        throw new IllegalArgumentException(\"Cannot delete map keys: \" + map);\n      } else if (updates.containsKey(keyId)) {\n        throw new IllegalArgumentException(\"Cannot update map keys: \" + map);\n      } else if (parentToAddedIds.containsKey(keyId)) {\n        throw new IllegalArgumentException(\"Cannot add fields to map keys: \" + map);\n      } else if (!map.keyType().equals(kResult)) {\n        throw new IllegalArgumentException(\"Cannot alter map keys: \" + map);\n      }\n\n      // use field to apply updates to the value\n      Types.NestedField valueField = map.fields().get(1);\n      Type valueType = field(valueField, valueResult);\n      if (valueType == null) {\n        throw new IllegalArgumentException(\"Cannot delete value type from map: \" + map);\n      }\n\n      Types.NestedField valueUpdate = updates.get(valueField.fieldId());\n      boolean isValueOptional =\n          valueUpdate != null ? valueUpdate.isOptional() : map.isValueOptional();\n\n      if (isValueOptional == map.isValueOptional() && map.valueType() == valueType) {\n        return map;\n      }\n\n      if (isValueOptional) {","sourceCodeStart":706,"sourceCodeEnd":742,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/SchemaUpdate.java#L706-L742","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Audit the update operations for anything touching the key field's type and remove it","Ensure updates are applied only to value fields and non-key columns","Build a new map column (drop + add) if a key type change is genuinely required"],"exampleFix":"// before\n// custom visitor that widens all int keys to long via updateColumn\nupdateSchema.updateColumn(mapValueParentFieldId, ...); // indirectly altering key type → throws\n// after\n// keep key type fixed; widen only the value\nupdateSchema.updateColumn(valueFieldId, Types.LongType.get());","handlingStrategy":"try-catch","validationCode":"Types.MapType map = ...;\nif (!map.keyType().equals(originalKeyType)) throw new IllegalArgumentException(\"Update must not change map key type \" + originalKeyType);","typeGuard":null,"tryCatchPattern":"try {\n  updateSchema.apply();\n} catch (IllegalArgumentException e) {\n  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);\n  throw e;\n}","preventionTips":["Keep map key types fixed across schema evolution; evolve only value types","Review multi-operation updates for transitive effects on key fields","If a key type change is unavoidable, plan a drop + re-add with data rewrite"],"tags":["schema","schema-evolution","validation"],"backgroundTag":"invalid-state-transition","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}