{"record":{"id":"70bdfdd1afcd3a39","repo":"apache/iceberg","slug":"table-metadata-refresh-is-required","errorCode":null,"errorMessage":"Table metadata refresh is required","messagePattern":"Table metadata refresh is required","errorType":"exception","errorClass":"CommitFailedException","httpStatus":null,"severity":"info","filePath":"core/src/main/java/org/apache/iceberg/BaseTransaction.java","lineNumber":486,"sourceCode":"  public class TransactionTableOperations implements TableOperations {\n    private TableOperations tempOps = ops.temp(current);\n\n    @Override\n    public TableMetadata current() {\n      return current;\n    }\n\n    @Override\n    public TableMetadata refresh() {\n      return current;\n    }\n\n    @Override\n    @SuppressWarnings(\"ConsistentOverrides\")\n    public void commit(TableMetadata underlyingBase, TableMetadata metadata) {\n      if (underlyingBase != current) {\n        // trigger a refresh and retry\n        throw new CommitFailedException(\"Table metadata refresh is required\");\n      }\n\n      BaseTransaction.this.current = metadata;\n\n      this.tempOps = ops.temp(metadata);\n\n      BaseTransaction.this.hasLastOpCommitted = true;\n    }\n\n    @Override\n    public FileIO io() {\n      return tempOps.io();\n    }\n\n    @Override\n    public EncryptionManager encryption() {\n      return tempOps.encryption();\n    }","sourceCodeStart":468,"sourceCodeEnd":504,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/BaseTransaction.java#L468-L504","documentation":"This CommitFailedException is thrown internally by the transaction's inner commit when the metadata the transaction was built on (underlyingBase) no longer matches the transaction's current metadata. It is a control-flow signal that triggers Iceberg's standard retry loop, which refreshes table metadata and re-applies updates. Callers normally never see it unless retries are exhausted.","triggerScenarios":"Another committer changes the table metadata between transaction start and commit, so the underlying table ops' current metadata differs from the transaction's base when commit(TableMetadata, TableMetadata) runs.","commonSituations":"Concurrent commits from other jobs/engines (e.g. compaction running while a transaction is open); external catalog refreshes; long transactions spanning metadata changes.","solutions":["No user action needed in the normal case — Iceberg retries and refreshes automatically","If retries are exhausted, refresh the table and re-run the transaction","Reduce the number of concurrent metadata writers to lower conflict frequency","Upgrade Iceberg if retry exhaustion occurs frequently"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"catch (CommitFailedException e) { table.refresh(); retryCommit(); } — the library's retry loop handles this; only handle at the outermost layer","preventionTips":["Minimize time between opening and committing a transaction","Avoid many concurrent jobs mutating the same table's metadata","Trust the built-in retry; do not suppress CommitFailedException"],"tags":["concurrency","retry","metadata-refresh"],"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-23T08:17:48.524Z"}