apache/iceberg · error · UnsupportedOperationException

does not support caching

Error message

 does not support caching

What it means

BaseDeleteLoader.getOrLoad is a hook for subclasses that implement delete-file caching; the base implementation does not cache and throws UnsupportedOperationException if called. It is only invoked after canCache(long) returned true, so calling this indicates a subclass claims caching support without implementing getOrLoad.

Solutions

  1. Implement getOrLoad in your subclass with an actual cache (e.g. Caffeine) honoring valueSize
  2. Return false from canCache if caching is not implemented
  3. Use a built-in loader subclass that supports caching

Example fix

// before
@Override
public boolean canCache(long contentSizeInBytes) {
  return true; // but getOrLoad not implemented
}
// after
@Override
public boolean canCache(long contentSizeInBytes) {
  return cache != null && contentSizeInBytes <= maxEntrySize;
}

@Override
protected <V> V getOrLoad(String key, Supplier<V> valueSupplier, long valueSize) {
  return cache.get(key, k -> valueSupplier.get());
}
Defensive patterns

Strategy: validation

Validate before calling

if (canCache(size) && getClass().getMethod("getOrLoad", String.class, Supplier.class, long.class).getDeclaringClass() == BaseDeleteLoader.class) { throw new IllegalStateException("caching enabled without getOrLoad override"); }

Try / catch

try { loader.getOrLoad(key, supplier, size); } catch (UnsupportedOperationException e) { // fall back to uncached read }

Prevention

When it happens

Trigger: A subclass of BaseDeleteLoader overrides canCache to return true but does not override getOrLoad; then getOrReadEqDeletes or indexes calls getOrLoad and hits the default throwing implementation.

Common situations: Custom delete loader implementations enabling caching incorrectly; copying a canCache override from another loader (e.g. a Spark cached loader) without the cache plumbing.

Related errors


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

Appendix: source

Thrown at data/src/main/java/org/apache/iceberg/data/BaseDeleteLoader.java:96

   * to use the provided size as a guideline to decide whether the value is eligible for caching.
   * For instance, it may be beneficial to discard values that are too large to optimize the cache
   * performance and utilization.
   */
  protected boolean canCache(long size) {
    return false;
  }

  /**
   * Gets the cached value for the key or populates the cache with a new mapping.
   *
   * <p>If the value for the specified key is in the cache, it should be returned. If the value is
   * not in the cache, implementations should compute the value using the provided supplier, cache
   * it, and then return it.
   *
   * <p>This method will be called only if {@link #canCache(long)} returned true.
   */
  protected <V> V getOrLoad(String key, Supplier<V> valueSupplier, long valueSize) {
    throw new UnsupportedOperationException(getClass().getName() + " does not support caching");
  }

  @Override
  public StructLikeSet loadEqualityDeletes(Iterable<DeleteFile> deleteFiles, Schema projection) {
    Iterable<Iterable<StructLike>> deletes =
        execute(deleteFiles, deleteFile -> getOrReadEqDeletes(deleteFile, projection));
    StructLikeSet deleteSet = StructLikeSet.create(projection.asStruct());
    Iterables.addAll(deleteSet, Iterables.concat(deletes));
    return deleteSet;
  }

  private Iterable<StructLike> getOrReadEqDeletes(DeleteFile deleteFile, Schema projection) {
    long estimatedSize = estimateEqDeletesSize(deleteFile, projection);
    if (canCache(estimatedSize)) {
      String cacheKey = deleteFile.location();
      return getOrLoad(cacheKey, () -> readEqDeletes(deleteFile, projection), estimatedSize);
    } else {
      return readEqDeletes(deleteFile, projection);

View on GitHub (pinned to 86d9c8fc54)