{"record":{"id":"cd5d300ccbbc4549","repo":"apache/iceberg","slug":"pendingupdatefailedexception","errorCode":null,"errorMessage":"PendingUpdateFailedException","messagePattern":"PendingUpdateFailedException","errorType":"exception","errorClass":"PendingUpdateFailedException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/BaseTransaction.java","lineNumber":450,"sourceCode":"  }\n\n  private void deleteUncommittedFiles(Iterable<String> paths) {\n    CatalogUtil.deleteFiles(ops.io(), paths, \"uncommitted\");\n  }\n\n  private void applyUpdates(TableOperations underlyingOps) {\n    if (base != underlyingOps.refresh()) {\n      // use refreshed the metadata\n      this.base = underlyingOps.current();\n      this.current = underlyingOps.current();\n      for (PendingUpdate update : updates) {\n        // re-commit each update in the chain to apply it and update current\n        try {\n          update.commit();\n        } catch (CommitFailedException e) {\n          // Cannot pass even with retry due to conflicting metadata changes. So, break the\n          // retry-loop.\n          throw new PendingUpdateFailedException(e);\n        }\n      }\n    }\n  }\n\n  // returns the manifest lists and manifests referenced by the given committed snapshots\n  private static Set<String> committedFiles(FileIO io, Set<Snapshot> snapshots) {\n    Set<String> committedFiles = Sets.newHashSet();\n\n    for (Snapshot snap : snapshots) {\n      committedFiles.add(snap.manifestListLocation());\n      snap.allManifests(io).forEach(manifest -> committedFiles.add(manifest.path()));\n    }\n\n    return committedFiles;\n  }\n\n  public class TransactionTableOperations implements TableOperations {","sourceCodeStart":432,"sourceCodeEnd":468,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/BaseTransaction.java#L432-L468","documentation":"PendingUpdateFailedException is thrown when a nested PendingUpdate inside a BaseTransaction fails to commit even after retrying, because of a conflicting metadata change. The transaction aborts rather than looping forever, wrapping the original CommitFailedException as the cause. It signals the whole transaction cannot be applied and the caller must start over from refreshed table metadata.","triggerScenarios":"Calling Transaction.commitTransaction() where an individual update's commit() repeatedly fails with CommitFailedException against concurrent metadata changes (e.g. conflicting schema updates, partition spec changes, or property writes from another committer).","commonSituations":"Concurrent writers to the same table (Spark jobs, Flink sinks, REST catalog commits) racing on table metadata; long-running transactions whose base metadata is stale by commit time; two transactions both altering schema or sort order.","solutions":["Refresh the table and re-create/re-run the whole transaction from the latest metadata","Reduce transaction scope so it commits quickly, minimizing the conflict window","Serialize or partition concurrent writers so they don't touch the same table metadata simultaneously","Inspect the wrapped CommitFailedException cause to identify which update is conflicting"],"exampleFix":"// before\nTransaction tx = table.newTransaction();\ntx.updateSchema().addColumn(\"new_col\", Types.LongType.get()).commit();\ntx.commitTransaction(); // may throw PendingUpdateFailedException\n// after\ntry {\n  Transaction tx = table.newTransaction();\n  tx.updateSchema().addColumn(\"new_col\", Types.LongType.get()).commit();\n  tx.commitTransaction();\n} catch (PendingUpdateFailedException e) {\n  table.refresh();\n  // rebuild and retry the transaction against fresh metadata\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"catch (PendingUpdateFailedException e) { table.refresh(); /* retry the whole transaction from fresh state */ } — always retry the full transaction, never individual updates","preventionTips":["Keep transactions short-lived to reduce commit-conflict windows","Avoid many concurrent writers mutating the same table's metadata","Catch and inspect getCause() to understand the conflicting update","Prefer row-level operations over metadata-level ones in hot paths"],"tags":["concurrency","transaction","commit-conflict"],"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"}