apache/iceberg · error · IllegalArgumentException

Invalid snapshot mode

Error message

Invalid snapshot mode: %s

What it means

CatalogHandlers.loadTable takes a SnapshotMode (the X-Iceberg-Access-Delegation / snapshot mode selector) and switches over it; if the mode value is not one of the known enum values the default branch throws IllegalArgumentException 'Invalid snapshot mode'.

Solutions

  1. Use a supported snapshot mode value (e.g. 'all' or 'main' as defined by SnapshotMode) in the request
  2. Upgrade the REST server to a version that knows the mode the client sends
  3. Check client configuration/property that sets the snapshot mode for typos

Example fix

// before
GET /v1/ns/tbl?X-Iceberg-Access-Delegation=snaphot
// after
GET /v1/ns/tbl?X-Iceberg-Access-Delegation=all
Defensive patterns

Strategy: validation

Validate before calling

// client: only send known modes
String mode = cfg.get("snapshot-mode");
if (!Set.of("all", "main").contains(mode)) throw new IllegalArgumentException("Unsupported mode: " + mode);

Try / catch

try {
  restClient.get(path + "?X-Iceberg-Access-Delegation=" + mode);
} catch (HttpClientException e) {
  if (e.status() == 400) { /* retry without snapshot mode */ }
}

Prevention

When it happens

Trigger: GET /v1/{prefix}/namespaces/{ns}/tables/{table} with an unrecognized snapshot mode value supplied by the client (e.g. misspelled or a mode added by a newer spec than the server supports).

Common situations: Newer client sending a snapshot mode the older server does not know; hand-rolled REST calls with wrong mode strings; config-driven mode selection with a bad value.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/693b7d4b29762788. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/org/apache/iceberg/rest/CatalogHandlers.java:525

    Table table = catalog.loadTable(ident);

    if (table instanceof BaseTable) {
      TableMetadata loadedMetadata = ((BaseTable) table).operations().current();

      TableMetadata metadata;
      switch (mode) {
        case ALL:
          metadata = loadedMetadata;
          break;
        case REFS:
          metadata =
              TableMetadata.buildFrom(loadedMetadata)
                  .withMetadataLocation(loadedMetadata.metadataFileLocation())
                  .suppressHistoricalSnapshots()
                  .build();
          break;
        default:
          throw new IllegalArgumentException(String.format("Invalid snapshot mode: %s", mode));
      }

      return LoadTableResponse.builder().withTableMetadata(metadata).build();
    } else if (table instanceof BaseMetadataTable) {
      // metadata tables are loaded on the client side, return NoSuchTableException for now
      throw new NoSuchTableException("Table does not exist: %s", ident);
    }

    throw new IllegalStateException("Cannot wrap catalog that does not produce BaseTable");
  }

  public static LoadTableResponse updateTable(
      Catalog catalog, TableIdentifier ident, UpdateTableRequest request) {
    TableMetadata finalMetadata;
    if (isCreate(request)) {
      // this is a hacky way to get TableOperations for an uncommitted table
      Transaction transaction =
          catalog.buildTable(ident, EMPTY_SCHEMA).createOrReplaceTransaction();

View on GitHub (pinned to 86d9c8fc54)