This is an automated email from the ASF dual-hosted git repository.
yufei pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg.git
The following commit(s) were added to refs/heads/main by this push:
new 88d833b06b Core: Handle NotFound exception for missing metadata file
(#13143)
88d833b06b is described below
commit 88d833b06bdb07101e458b8fdb7e60fe9e1a9c9a
Author: Ashok <[email protected]>
AuthorDate: Thu Jan 8 07:00:01 2026 +0530
Core: Handle NotFound exception for missing metadata file (#13143)
---
.../org/apache/iceberg/rest/ErrorHandlers.java | 3 ++
.../org/apache/iceberg/catalog/CatalogTests.java | 38 ++++++++++++++++++++++
.../iceberg/inmemory/TestInMemoryCatalog.java | 30 +++++++++++++++++
.../apache/iceberg/rest/RESTCatalogAdapter.java | 2 ++
.../org/apache/iceberg/rest/TestRESTCatalog.java | 22 +++++++++++++
5 files changed, 95 insertions(+)
diff --git a/core/src/main/java/org/apache/iceberg/rest/ErrorHandlers.java
b/core/src/main/java/org/apache/iceberg/rest/ErrorHandlers.java
index b1575035fc..543e548529 100644
--- a/core/src/main/java/org/apache/iceberg/rest/ErrorHandlers.java
+++ b/core/src/main/java/org/apache/iceberg/rest/ErrorHandlers.java
@@ -31,6 +31,7 @@ import org.apache.iceberg.exceptions.NoSuchPlanTaskException;
import org.apache.iceberg.exceptions.NoSuchTableException;
import org.apache.iceberg.exceptions.NoSuchViewException;
import org.apache.iceberg.exceptions.NotAuthorizedException;
+import org.apache.iceberg.exceptions.NotFoundException;
import org.apache.iceberg.exceptions.RESTException;
import org.apache.iceberg.exceptions.ServiceFailureException;
import org.apache.iceberg.exceptions.ServiceUnavailableException;
@@ -124,6 +125,8 @@ public class ErrorHandlers {
case 404:
if
(NoSuchNamespaceException.class.getSimpleName().equals(error.type())) {
throw new NoSuchNamespaceException("%s", error.message());
+ } else if
(NotFoundException.class.getSimpleName().equals(error.type())) {
+ throw new NotFoundException("%s", error.message());
} else {
throw new NoSuchTableException("%s", error.message());
}
diff --git a/core/src/test/java/org/apache/iceberg/catalog/CatalogTests.java
b/core/src/test/java/org/apache/iceberg/catalog/CatalogTests.java
index 5d20bc15a9..833b2fb0b4 100644
--- a/core/src/test/java/org/apache/iceberg/catalog/CatalogTests.java
+++ b/core/src/test/java/org/apache/iceberg/catalog/CatalogTests.java
@@ -26,6 +26,11 @@ import static org.assertj.core.api.Assumptions.assumeThat;
import java.io.IOException;
import java.io.UncheckedIOException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -63,6 +68,7 @@ import org.apache.iceberg.exceptions.CommitFailedException;
import org.apache.iceberg.exceptions.NamespaceNotEmptyException;
import org.apache.iceberg.exceptions.NoSuchNamespaceException;
import org.apache.iceberg.exceptions.NoSuchTableException;
+import org.apache.iceberg.exceptions.NotFoundException;
import org.apache.iceberg.expressions.Expressions;
import org.apache.iceberg.expressions.Literal;
import org.apache.iceberg.io.CloseableIterable;
@@ -81,6 +87,7 @@ import
org.apache.iceberg.relocated.com.google.common.collect.Streams;
import org.apache.iceberg.types.Types;
import org.apache.iceberg.util.CharSequenceSet;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@@ -960,6 +967,37 @@ public abstract class CatalogTests<C extends Catalog &
SupportsNamespaces> {
.hasMessageStartingWith("Table does not exist: ns.tbl");
}
+ @Test
+ public void testLoadTableWithMissingMetadataFile(@TempDir Path tempDir)
throws IOException {
+ C catalog = catalog();
+
+ if (requiresNamespaceCreate()) {
+ catalog.createNamespace(TBL.namespace());
+ }
+
+ catalog.buildTable(TBL, SCHEMA).create();
+ assertThat(catalog.tableExists(TBL)).as("Table should exist").isTrue();
+
+ Table table = catalog.loadTable(TBL);
+ String metadataFileLocation =
+ ((HasTableOperations)
table).operations().current().metadataFileLocation();
+ Path renamedMetadataFile = tempDir.resolve("tmp.json");
+ renamedMetadataFile.toFile().deleteOnExit();
+ Files.writeString(renamedMetadataFile, "metadata");
+ Path metadataFilePath =
+ metadataFileLocation.startsWith("file:")
+ ? Paths.get(URI.create(metadataFileLocation))
+ : Paths.get(metadataFileLocation);
+ try {
+ Files.move(metadataFilePath, renamedMetadataFile,
StandardCopyOption.REPLACE_EXISTING);
+ assertThatThrownBy(() -> catalog.loadTable(TBL))
+ .isInstanceOf(NotFoundException.class)
+ .hasMessageContaining("Failed to open input stream for file: " +
metadataFileLocation);
+ } finally {
+ Files.move(renamedMetadataFile, metadataFilePath,
StandardCopyOption.REPLACE_EXISTING);
+ }
+ }
+
@Test
public void testRenameTable() {
C catalog = catalog();
diff --git
a/core/src/test/java/org/apache/iceberg/inmemory/TestInMemoryCatalog.java
b/core/src/test/java/org/apache/iceberg/inmemory/TestInMemoryCatalog.java
index 705ff3dc86..c2c683e7d8 100644
--- a/core/src/test/java/org/apache/iceberg/inmemory/TestInMemoryCatalog.java
+++ b/core/src/test/java/org/apache/iceberg/inmemory/TestInMemoryCatalog.java
@@ -18,11 +18,21 @@
*/
package org.apache.iceberg.inmemory;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.io.IOException;
+import java.nio.file.Path;
import java.util.Map;
import org.apache.iceberg.CatalogProperties;
+import org.apache.iceberg.HasTableOperations;
+import org.apache.iceberg.Table;
import org.apache.iceberg.catalog.CatalogTests;
+import org.apache.iceberg.exceptions.NotFoundException;
import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
public class TestInMemoryCatalog extends CatalogTests<InMemoryCatalog> {
private InMemoryCatalog catalog;
@@ -71,4 +81,24 @@ public class TestInMemoryCatalog extends
CatalogTests<InMemoryCatalog> {
protected boolean supportsEmptyNamespace() {
return true;
}
+
+ @Test
+ @Override
+ public void testLoadTableWithMissingMetadataFile(@TempDir Path tempDir)
throws IOException {
+
+ if (requiresNamespaceCreate()) {
+ catalog.createNamespace(TBL.namespace());
+ }
+
+ catalog.buildTable(TBL, SCHEMA).create();
+ assertThat(catalog.tableExists(TBL)).as("Table should exist").isTrue();
+ Table table = catalog.loadTable(TBL);
+ String metadataFileLocation =
+ ((HasTableOperations)
table).operations().current().metadataFileLocation();
+ table.io().deleteFile(metadataFileLocation);
+ assertThatThrownBy(() -> catalog.loadTable(TBL))
+ .isInstanceOf(NotFoundException.class)
+ .hasMessage("No in-memory file found for location: " +
metadataFileLocation);
+ table.io().newOutputFile(metadataFileLocation).create();
+ }
}
diff --git a/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java
b/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java
index 524b3e760c..e62937b6df 100644
--- a/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java
+++ b/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java
@@ -53,6 +53,7 @@ import org.apache.iceberg.exceptions.NoSuchPlanTaskException;
import org.apache.iceberg.exceptions.NoSuchTableException;
import org.apache.iceberg.exceptions.NoSuchViewException;
import org.apache.iceberg.exceptions.NotAuthorizedException;
+import org.apache.iceberg.exceptions.NotFoundException;
import org.apache.iceberg.exceptions.RESTException;
import org.apache.iceberg.exceptions.UnprocessableEntityException;
import org.apache.iceberg.exceptions.ValidationException;
@@ -97,6 +98,7 @@ public class RESTCatalogAdapter extends BaseHTTPClient {
.put(ForbiddenException.class, 403)
.put(NoSuchNamespaceException.class, 404)
.put(NoSuchTableException.class, 404)
+ .put(NotFoundException.class, 404)
.put(NoSuchViewException.class, 404)
.put(NoSuchIcebergTableException.class, 404)
.put(UnsupportedOperationException.class, 406)
diff --git a/core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java
b/core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java
index df4ba3214a..d202680e56 100644
--- a/core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java
+++ b/core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java
@@ -59,6 +59,7 @@ import org.apache.iceberg.BaseTransaction;
import org.apache.iceberg.CatalogProperties;
import org.apache.iceberg.DataFile;
import org.apache.iceberg.DataFiles;
+import org.apache.iceberg.HasTableOperations;
import org.apache.iceberg.MetadataUpdate;
import org.apache.iceberg.PartitionSpec;
import org.apache.iceberg.Schema;
@@ -3453,6 +3454,27 @@ public class TestRESTCatalog extends
CatalogTests<RESTCatalog> {
return local;
}
+ @Test
+ @Override
+ public void testLoadTableWithMissingMetadataFile(@TempDir Path tempDir) {
+
+ if (requiresNamespaceCreate()) {
+ restCatalog.createNamespace(TBL.namespace());
+ }
+
+ restCatalog.buildTable(TBL, SCHEMA).create();
+ assertThat(restCatalog.tableExists(TBL)).as("Table should exist").isTrue();
+
+ Table table = restCatalog.loadTable(TBL);
+ String metadataFileLocation =
+ ((HasTableOperations)
table).operations().current().metadataFileLocation();
+ table.io().deleteFile(metadataFileLocation);
+
+ assertThatThrownBy(() -> restCatalog.loadTable(TBL))
+ .isInstanceOf(NotFoundException.class)
+ .hasMessageContaining("No in-memory file found for location: " +
metadataFileLocation);
+ }
+
private RESTCatalog catalog(RESTCatalogAdapter adapter) {
RESTCatalog catalog =
new RESTCatalog(SessionCatalog.SessionContext.createEmpty(), (config)
-> adapter);