Skip to content

Commit 40363fc

Browse files
committed
REST: treat HTTP 400 commit-validation failures as CommitFailedException
Some REST catalog implementations (e.g., Databricks Unity Catalog) return HTTP 400 with a "commit validation failed" message for concurrent-write conflicts instead of the spec-mandated HTTP 409. Because CommitErrorHandler previously mapped all 400 responses to BadRequestException, these conflicts escaped SnapshotProducer's retry-with-refresh loop entirely and propagated as fatal errors. Add a case 400 check in CommitErrorHandler that recognises the conflict pattern and raises CommitFailedException instead, restoring normal optimistic-concurrency retry behaviour for non-compliant catalogs. Responses that do not match the pattern still fall through to the default 400 handler and raise BadRequestException as before.
1 parent 6976e02 commit 40363fc

1 file changed

Lines changed: 13 additions & 0 deletions

File tree

core/src/main/java/org/apache/iceberg/rest/ErrorHandlers.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package org.apache.iceberg.rest;
2020

21+
import java.util.Locale;
2122
import java.util.function.Consumer;
2223
import org.apache.iceberg.exceptions.AlreadyExistsException;
2324
import org.apache.iceberg.exceptions.BadRequestException;
@@ -122,6 +123,18 @@ private static class CommitErrorHandler extends DefaultErrorHandler {
122123
@Override
123124
public void accept(ErrorResponse error) {
124125
switch (error.code()) {
126+
case 400:
127+
// Some REST catalog implementations (e.g., Databricks Unity Catalog) return HTTP 400
128+
// with a commit-conflict message instead of the spec-mandated 409. Treat such responses
129+
// as CommitFailedException so the standard retry-with-refresh loop can handle them.
130+
// Responses that indicate a genuinely malformed request (no commit-related message) still
131+
// fall through to the default 400 handler which raises BadRequestException.
132+
if (error.message() != null
133+
&& error.message().toLowerCase(Locale.ROOT).contains("commit")
134+
&& error.message().toLowerCase(Locale.ROOT).contains("validation failed")) {
135+
throw new CommitFailedException("Commit failed: %s", error.message());
136+
}
137+
break;
125138
case 404:
126139
throw new NoSuchTableException("%s", error.message());
127140
case 409:

0 commit comments

Comments
 (0)