apache/iceberg · error · UnsupportedOperationException

Cannot unset reserved property

Error message

Cannot unset reserved property: '%s'

What it means

SparkCatalog.alterView rejects ViewChange.RemoveProperty (and SetProperty) changes whose property name is in SparkView.RESERVED_PROPERTIES (e.g. query, schema, dialect, queryColumnNames managed by Iceberg). verifyNonReservedProperty (SparkCatalog.java:725) throws UnsupportedOperationException with 'Cannot unset reserved property: <name>' because those properties are derived from the view definition and must never be edited directly.

Solutions

  1. Remove the reserved key from your UNSET list; reserved properties are managed by the engine and cannot be changed via ALTER VIEW.
  2. To change 'query' or 'schema', redefine the view with CREATE OR REPLACE VIEW instead of altering properties.
  3. Filter properties client-side against SparkView.RESERVED_PROPERTIES before issuing alterView.
  4. Store custom metadata under a non-reserved, prefixed key (e.g. 'custom.owner') and unset that instead.

Example fix

// before
spark.sql("ALTER VIEW analytics.v UNSET TBLPROPERTIES ('query')"); // UnsupportedOperationException

// after
spark.sql("ALTER VIEW analytics.v UNSET TBLPROPERTIES ('custom.owner')");
// redefine reserved metadata instead:
spark.sql("CREATE OR REPLACE VIEW analytics.v AS SELECT ...");
Defensive patterns

Strategy: validation

Validate before calling

Set<String> reserved = SparkView.RESERVED_PROPERTIES;
List<String> safeKeys = propsToUnset.stream()
    .filter(k -> !reserved.contains(k))
    .collect(Collectors.toList());
// only issue UNSET for safeKeys

Type guard

boolean isUnsettable(String property) {
  return !SparkView.RESERVED_PROPERTIES.contains(property);
}

Try / catch

try {
  viewChangeRemover.apply(ident, properties);
} catch (UnsupportedOperationException e) {
  if (e.getMessage().startsWith("Cannot unset reserved property")) {
    // skip the reserved key and continue with the remaining properties
  }
}

Prevention

When it happens

Trigger: Calling ALTER VIEW <ident> UNSET TBLPROPERTIES ('query') or the programmatic equivalent alterView(ident, new ViewChange.RemoveProperty("query")) — likewise for 'schema', 'dialect', 'description-of-query columns' such as SparkView.QUERY_COLUMN_NAMES — where the property matches a reserved name in SparkView.RESERVED_PROPERTIES.

Common situations: Cleanup scripts that iterate all TBLPROPERTIES and UNSET each one, blindly hitting reserved keys; migrating views from Hive where 'query'/'schema' were user-settable; templated UNSET commands with a hardcoded property list that includes reserved names.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at spark/v3.5/spark/src/main/java/org/apache/iceberg/spark/SparkCatalog.java:727

            updateViewProperties.remove(remove.property());
          }
        }

        updateViewProperties.commit();

        return new SparkView(catalogName, view);
      } catch (org.apache.iceberg.exceptions.NoSuchViewException e) {
        throw new NoSuchViewException(ident);
      }
    }

    throw new UnsupportedOperationException(
        "Altering a view is not supported by catalog: " + catalogName);
  }

  private static void verifyNonReservedProperty(String property, String errorMsg) {
    if (SparkView.RESERVED_PROPERTIES.contains(property)) {
      throw new UnsupportedOperationException(String.format(errorMsg, property));
    }
  }

  private static void verifyNonReservedPropertyIsUnset(String property) {
    verifyNonReservedProperty(property, "Cannot unset reserved property: '%s'");
  }

  private static void verifyNonReservedPropertyIsSet(String property) {
    verifyNonReservedProperty(property, "Cannot set reserved property: '%s'");
  }

  @Override
  public boolean dropView(Identifier ident) {
    if (null != asViewCatalog) {
      return asViewCatalog.dropView(buildIdentifier(ident));
    }

    return false;

View on GitHub (pinned to 86d9c8fc54)