apache/iceberg · error · UnsupportedOperationException

Cannot call refresh on temporary table operations

Error message

Cannot call refresh on temporary table operations

What it means

TemporaryTableOperations (the anonymous subclass returned for uncommitted transaction metadata) cannot reload state from a metastore because it only holds in-memory, uncommitted metadata. Its refresh() override therefore throws UnsupportedOperationException to make clear that refresh semantics do not apply to temporary operations.

Source

Thrown at core/src/main/java/org/apache/iceberg/BaseMetastoreTableOperations.java:248

    return metadataFileLocation(current(), filename);
  }

  @Override
  public LocationProvider locationProvider() {
    return LocationProviders.locationsFor(current().location(), current().properties());
  }

  @Override
  public TableOperations temp(TableMetadata uncommittedMetadata) {
    return new TableOperations() {
      @Override
      public TableMetadata current() {
        return uncommittedMetadata;
      }

      @Override
      public TableMetadata refresh() {
        throw new UnsupportedOperationException(
            "Cannot call refresh on temporary table operations");
      }

      @Override
      public void commit(TableMetadata base, TableMetadata metadata) {
        throw new UnsupportedOperationException("Cannot call commit on temporary table operations");
      }

      @Override
      public String metadataFileLocation(String fileName) {
        return BaseMetastoreTableOperations.this.metadataFileLocation(
            uncommittedMetadata, fileName);
      }

      @Override
      public LocationProvider locationProvider() {
        return LocationProviders.locationsFor(
            uncommittedMetadata.location(), uncommittedMetadata.properties());

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Do not call refresh on temporary table operations; read uncommittedMetadata via current() instead
  2. Commit the staged transaction through the real catalog first, then operate on the resulting table's operations
  3. Restructure code to distinguish TableOperations from temporary/staged operations before refreshing

Example fix

// before
tempOps.refresh(); // UnsupportedOperationException
TableMetadata meta = tempOps.current();
// after
TableMetadata meta = tempOps.current(); // temporary ops already expose in-memory metadata
// refresh only on real, metastore-backed TableOperations
Defensive patterns

Strategy: type-guard

Validate before calling

if (isTemporaryOps(ops)) { readFromCurrentMetadata(ops); } else { ops.refresh(); }

Type guard

boolean isTemporaryOps(TableOperations ops) { return ops instanceof BaseMetastoreTableOperations temp && !supportsRealRefresh; /* or track a flag when constructing temporary ops */ }

Try / catch

try { ops.refresh(); }
catch (UnsupportedOperationException e) { /* temporary ops: use current() metadata instead */ }

Prevention

When it happens

Trigger: Calling refresh() on TableOperations obtained from a temporary/staged table (e.g., transaction staging APIs or uncommitted metadata snapshots); any code path that calls current() triggering an internal refresh request on temporary ops.

Common situations: Staged/branch-like transaction workflows (stageCreate-like usage) where code treats the temporary ops like normal table ops; utility code that unconditionally refreshes before reading; tests that mix real and temporary operations.

Related errors


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