apache/iceberg · warning · CommitFailedException
Failed to update table %s from catalog %s
Error message
Failed to update table %s from catalog %s
What it means
updateTable performs an atomic UPDATE of the metadata location guarded by the old location (WHERE metadata_location = oldMetadataLocation). If exactly one row is not updated (updatedRecords != 1) the concurrent state no longer matches the base, so a CommitFailedException is thrown. This is Iceberg's normal optimistic-concurrency retry signal: the caller (BaseTable/CommitState) is expected to refresh and retry the commit.
Source
Thrown at core/src/main/java/org/apache/iceberg/jdbc/JdbcTableOperations.java:160
throw new UncheckedInterruptedException(e, "Interrupted during commit");
}
}
private void updateTable(String newMetadataLocation, String oldMetadataLocation)
throws SQLException, InterruptedException {
int updatedRecords =
JdbcUtil.updateTable(
schemaVersion,
connections,
catalogName,
tableIdentifier,
newMetadataLocation,
oldMetadataLocation);
if (updatedRecords == 1) {
LOG.debug("Successfully committed to existing table: {}", tableIdentifier);
} else {
throw new CommitFailedException(
"Failed to update table %s from catalog %s", tableIdentifier, catalogName);
}
}
private void createTable(String newMetadataLocation) throws SQLException, InterruptedException {
Namespace namespace = tableIdentifier.namespace();
if (PropertyUtil.propertyAsBoolean(catalogProperties, JdbcUtil.STRICT_MODE_PROPERTY, false)
&& !JdbcUtil.namespaceExists(catalogName, connections, namespace)) {
throw new NoSuchNamespaceException(
"Cannot create table %s in catalog %s. Namespace %s does not exist",
tableIdentifier, catalogName, namespace);
}
if (schemaVersion == JdbcUtil.SchemaVersion.V1
&& JdbcUtil.viewExists(catalogName, connections, tableIdentifier)) {
throw new AlreadyExistsException("View with same name already exists: %s", tableIdentifier);
}
View on GitHub (pinned to 86d9c8fc54)
Solutions
- Retry the commit with a refreshed table: load the table again so base reflects the latest metadata, then reapply changes (Iceberg's commit retry loop does this automatically)
- Reduce write contention or partition writes so concurrent commits to one table are rare
- Check identifier casing against the DB's case-sensitivity rules if retries never succeed
- If the table was recreated, re-point the job at the new table instead of committing stale state
Example fix
// before
Table table = catalog.loadTable(ident);
table.refresh();
table.newAppend().appendFile(f).commit(); // single shot, fails on race
// after
Tasks.foreach(table)
.retry(10)
.onlyRetryOn(CommitFailedException.class)
.run(t -> t.newAppend().appendFile(f).commit()); Defensive patterns
Strategy: retry
Validate before calling
table.refresh(); // ensure base metadata location matches catalog before commit
Try / catch
Tasks.foreach(table).retry(10).onlyRetryOn(CommitFailedException.class).run(t -> t.transaction().commitTransaction());
Prevention
- Always commit through Iceberg's retrying commit path; never wrap in a single-shot call
- Refresh tables before commits in long-running jobs
- Minimize concurrent writers per table or partition workloads
- Keep identifiers' casing consistent with the DB collation
When it happens
Trigger: Two writers committed between this job's refresh and its commit, so the row's metadata_location no longer equals the base location; the table was dropped and recreated; the UPDATE matched 0 rows due to wrong tableIdentifier/catalogName casing on case-sensitive DBs.
Common situations: Multiple Spark/Flink jobs writing to the same table concurrently; a compaction job racing with streaming ingest; manual metadata file edits; case-sensitive Postgres vs case-insensitive MySQL identifier mismatches.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Failed to create table %s in catalog %s
- Cannot commit %s: metadata location %s has changed from %s
- Unknown failure
- Interrupted during commit
- Unknown failure
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/807dc1d179308b3b.
Report an issue: GitHub.