{"record":{"id":"380fb62c08960f07","repo":"apache/iceberg","slug":"requirement-failed-last-assigned-field-id-changed","errorCode":null,"errorMessage":"Requirement failed: last assigned field id changed: expected id %s != %s","messagePattern":"Requirement failed: last assigned field id changed: expected id (.+?) != (.+?)","errorType":"exception","errorClass":"CommitFailedException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/UpdateRequirement.java","lineNumber":143,"sourceCode":"      }\n    }\n  }\n\n  class AssertLastAssignedFieldId implements UpdateRequirement {\n    private final int lastAssignedFieldId;\n\n    public AssertLastAssignedFieldId(int lastAssignedFieldId) {\n      this.lastAssignedFieldId = lastAssignedFieldId;\n    }\n\n    public int lastAssignedFieldId() {\n      return lastAssignedFieldId;\n    }\n\n    @Override\n    public void validate(TableMetadata base) {\n      if (base != null && base.lastColumnId() != lastAssignedFieldId) {\n        throw new CommitFailedException(\n            \"Requirement failed: last assigned field id changed: expected id %s != %s\",\n            lastAssignedFieldId, base.lastColumnId());\n      }\n    }\n  }\n\n  class AssertCurrentSchemaID implements UpdateRequirement {\n    private final int schemaId;\n\n    public AssertCurrentSchemaID(int schemaId) {\n      this.schemaId = schemaId;\n    }\n\n    public int schemaId() {\n      return schemaId;\n    }\n\n    @Override","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/UpdateRequirement.java#L125-L161","documentation":"Thrown by AssertLastAssignedFieldId.validate when the requirement's lastAssignedFieldId does not match the base table's lastColumnId (the highest assigned nested-field ID). Field IDs are monotonic and must never be reused; this assertion guarantees no concurrent schema change assigned new field IDs between planning and commit. A mismatch means another writer added/dropped columns concurrently, so the commit fails.","triggerScenarios":"Committing schema updates with UpdateRequirements including AssertLastAssignedFieldId when base.lastColumnId() differs from the asserted value — any concurrent AddColumn/UpdateColumn operation on the table invalidates the assertion.","commonSituations":"Two jobs evolve the schema simultaneously (e.g. a streaming pipeline adding columns while a batch job alters types); REST catalog commits from a stale client-side cached table; schema-drift tools (e.g. schema registries auto-adding fields) racing with manual ALTER TABLE.","solutions":["Refresh the table, re-read lastColumnId, rebuild requirements and re-apply the schema change, then retry the commit.","Re-apply the schema update on top of the refreshed schema so new field IDs are assigned correctly (never reuse asserted IDs).","Coordinate schema evolution through a single owner or migration tool to avoid concurrent column additions.","Catch CommitFailedException and retry with backoff, as Iceberg commits are designed to be retried against fresh metadata."],"exampleFix":"// before\nList<UpdateRequirement> reqs = ImmutableList.of(\n    new UpdateRequirement.AssertLastAssignedFieldId(42)); // stale\n// after\ntable.refresh();\nList<UpdateRequirement> reqs = ImmutableList.of(\n    new UpdateRequirement.AssertLastAssignedFieldId(table.schema().highestFieldId()));","handlingStrategy":"retry","validationCode":"table.refresh();\nif (table.schema().highestFieldId() != assertedFieldId) {\n  // rebuild requirements from current metadata before committing\n}","typeGuard":null,"tryCatchPattern":"try {\n  schemaUpdate.commit();\n} catch (CommitFailedException e) {\n  if (e.getMessage().contains(\"last assigned field id changed\")) {\n    table.refresh();\n    reapplySchemaChange(table); // retry with backoff\n  } else { throw e; }\n}","preventionTips":["Route all schema evolution through one owner/pipeline","Refresh the table before schema updates","Never reuse hardcoded field IDs; derive from current schema","Implement CommitFailedException retry with backoff for all metadata commits"],"tags":["concurrency","schema-evolution","commit-failed","iceberg"],"backgroundTag":"schema-validation-failed","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"}