{"record":{"id":"a72e28313062576c","repo":"apache/iceberg","slug":"cannot-commit-stale-table-metadata","errorCode":null,"errorMessage":"Cannot commit: stale table metadata","messagePattern":"Cannot commit: stale table metadata","errorType":"exception","errorClass":"CommitFailedException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/BaseMetastoreTableOperations.java","lineNumber":113,"sourceCode":"\n      currentMetadata = null;\n      currentMetadataLocation = null;\n      version = -1;\n      throw e;\n    }\n    return current();\n  }\n\n  protected void doRefresh() {\n    throw new UnsupportedOperationException(\"Not implemented: doRefresh\");\n  }\n\n  @Override\n  public void commit(TableMetadata base, TableMetadata metadata) {\n    // if the metadata is already out of date, reject it\n    if (base != current()) {\n      if (base != null) {\n        throw new CommitFailedException(\"Cannot commit: stale table metadata\");\n      } else {\n        // when current is non-null, the table exists. but when base is null, the commit is trying\n        // to create the table\n        throw new AlreadyExistsException(\"Table already exists: %s\", tableName());\n      }\n    }\n    // if the metadata is not changed, return early\n    if (base == metadata) {\n      LOG.info(\"Nothing to commit.\");\n      return;\n    }\n\n    long start = System.currentTimeMillis();\n    doCommit(base, metadata);\n    CatalogUtil.deleteRemovedMetadataFiles(io(), base, metadata);\n    requestRefresh();\n\n    LOG.info(","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/BaseMetastoreTableOperations.java#L95-L131","documentation":"commit(base, metadata) rejects a commit when the supplied base metadata is non-null but differs from the currently cached metadata (by reference), meaning the table changed since the caller read it. This optimistic-concurrency check prevents silently overwriting intermediate updates; the caller must refresh and reapply its changes.","triggerScenarios":"A long-running transaction or retry loop passes a TableMetadata base that no longer matches current(), e.g. after another commit landed or after refresh invalidated the cached base.","commonSituations":"Two jobs committing to the same table concurrently; a retry loop reusing a stale base across attempts; holding a Table reference across external catalog updates (schema updates by another engine) before committing.","solutions":["Refresh operations (ops.refresh()) and rebuild base from the current metadata, then reapply changes","Use the table's Transaction API, which manages base/current tracking for you","Catch CommitFailedException in the commit retry loop and restart with fresh metadata","Coordinate writers so only one process commits at a time"],"exampleFix":"// before\nops.commit(staleBase, newMetadata); // CommitFailedException\n// after\nops.refresh();\nTableMetadata freshBase = ops.current();\nTableMetadata rebased = TableMetadata.buildFrom(freshBase)\n    .setCurrentSchema(newSchema, newSchema.highestFieldId())\n    .build();\nops.commit(freshBase, rebased);","handlingStrategy":"retry","validationCode":"ops.refresh(); // ensure cached base matches current before building the commit","typeGuard":null,"tryCatchPattern":"boolean done = Tasks.foreach(...).retry(3).onlyRetryOn(CommitFailedException.class).run(...) — or:\ntry { ops.commit(base, metadata); }\ncatch (CommitFailedException e) { ops.refresh(); base = ops.current(); /* reapply and retry */ }","preventionTips":["Re-read base metadata immediately before commit","Use the Table/Transaction APIs instead of raw commit","Keep commit windows short; avoid long-lived Table references across external changes"],"tags":["concurrency","commit-failed","stale-metadata"],"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"}