{"record":{"id":"4830aa587ff6d6e8","repo":"apache/iceberg","slug":"cannot-specify-the-sort-order-because-it-s-a-res-4830aa","errorCode":null,"errorMessage":"Cannot specify the 'sort-order' because it's a reserved table property. Please use the command 'ALTER TABLE ... WRITE ORDERED BY' to specify write sort-orders.","messagePattern":"Cannot specify the 'sort-order' because it's a reserved table property\\. Please use the command 'ALTER TABLE \\.\\.\\. WRITE ORDERED BY' to specify write sort-orders\\.","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/SparkCatalog.java","lineNumber":342,"sourceCode":"  @Override\n  public Table alterTable(Identifier ident, TableChange... changes) throws NoSuchTableException {\n    SetProperty setLocation = null;\n    SetProperty setSnapshotId = null;\n    SetProperty pickSnapshotId = null;\n    List<TableChange> propertyChanges = Lists.newArrayList();\n    List<TableChange> schemaChanges = Lists.newArrayList();\n\n    for (TableChange change : changes) {\n      if (change instanceof SetProperty) {\n        SetProperty set = (SetProperty) change;\n        if (TableCatalog.PROP_LOCATION.equalsIgnoreCase(set.property())) {\n          setLocation = set;\n        } else if (\"current-snapshot-id\".equalsIgnoreCase(set.property())) {\n          setSnapshotId = set;\n        } else if (\"cherry-pick-snapshot-id\".equalsIgnoreCase(set.property())) {\n          pickSnapshotId = set;\n        } else if (\"sort-order\".equalsIgnoreCase(set.property())) {\n          throw new UnsupportedOperationException(\n              \"Cannot specify the 'sort-order' because it's a reserved table \"\n                  + \"property. Please use the command 'ALTER TABLE ... WRITE ORDERED BY' to specify write sort-orders.\");\n        } else if (\"identifier-fields\".equalsIgnoreCase(set.property())) {\n          throw new UnsupportedOperationException(\n              \"Cannot specify the 'identifier-fields' because it's a reserved table property. \"\n                  + \"Please use the command 'ALTER TABLE ... SET IDENTIFIER FIELDS' to specify identifier fields.\");\n        } else {\n          propertyChanges.add(set);\n        }\n      } else if (change instanceof RemoveProperty) {\n        propertyChanges.add(change);\n      } else if (change instanceof ColumnChange) {\n        schemaChanges.add(change);\n      } else {\n        throw new UnsupportedOperationException(\"Cannot apply unknown table change: \" + change);\n      }\n    }\n","sourceCodeStart":324,"sourceCodeEnd":360,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/SparkCatalog.java#L324-L360","documentation":"During ALTER TABLE, SparkCatalog.alterTable inspects SetProperty changes and rejects attempts to set 'sort-order', which is a reserved internal Iceberg property managed through a dedicated SQL clause, not a plain table property.","triggerScenarios":"ALTER TABLE ... SET TBLPROPERTIES ('sort-order' = '...') or a TableChange.setProperty(\"sort-order\", ...) API call.","commonSituations":"Copying table properties (e.g. via SHOW TBLPROPERTIES dump and re-apply scripts) where the read-only 'sort-order' property gets replayed as a set; hand-written migration scripts serializing all properties.","solutions":["Use ALTER TABLE ... WRITE ORDERED BY to define write sort orders instead of setting the property.","Filter out 'sort-order' (and other reserved properties) when replaying property dumps.","Drop the property assignment from the migration script if sort order is already defined.","Remove the sort order via WRITE ORDERED BY with no columns if clearing is desired — not via property change."],"exampleFix":"// before\nspark.sql(\"ALTER TABLE prod.db.events SET TBLPROPERTIES ('sort-order'='id ASC')\");\n// after\nspark.sql(\"ALTER TABLE prod.db.events WRITE ORDERED BY id\");","handlingStrategy":"validation","validationCode":"private static final Set<String> RESERVED = Set.of(\"location\", \"current-snapshot-id\",\n    \"cherry-pick-snapshot-id\", \"sort-order\", \"identifier-fields\");\nif (RESERVED.contains(propKey.toLowerCase(Locale.ROOT))) {\n  throw new IllegalArgumentException(\"Reserved property: \" + propKey);\n}","typeGuard":null,"tryCatchPattern":"try {\n  spark.sql(alterSql);\n} catch (UnsupportedOperationException e) {\n  if (e.getMessage().contains(\"'sort-order'\")) {\n    throw new IllegalArgumentException(\"Use ALTER TABLE ... WRITE ORDERED BY for sort orders\", e);\n  }\n  throw e;\n}","preventionTips":["Never replay ALL properties from SHOW TBLPROPERTIES; filter reserved keys first.","Use WRITE ORDERED BY for sort orders and dedicated clauses for other managed metadata.","Keep a constant set of reserved Iceberg properties in migration tooling.","Review generated ALTER scripts before execution."],"tags":["spark","iceberg","alter-table","reserved-property"],"backgroundTag":"unsupported-operation","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"}