apache/iceberg · error · UnsupportedOperationException

hash(value) is not supported on the base Bucket class

Error message

hash(value) is not supported on the base Bucket class

What it means

Bucket<T> is an abstract base; actual hashing is implemented by per-type subclasses (BucketInt, BucketString, etc.) and reached through bind(type), which returns the type-specific function. Calling the protected hash(value) on the base class means no concrete type was bound, so Iceberg throws UnsupportedOperationException. Always obtain a bound function via bind() before hashing or applying.

Source

Thrown at api/src/main/java/org/apache/iceberg/transforms/Bucket.java:99

  private final int numBuckets;

  private Bucket(int numBuckets) {
    this.numBuckets = numBuckets;
  }

  public Integer numBuckets() {
    return numBuckets;
  }

  @Override
  public SerializableFunction<T, Integer> bind(Type type) {
    Preconditions.checkArgument(canTransform(type), "Cannot bucket by type: %s", type);
    return get(type, numBuckets);
  }

  protected int hash(T value) {
    throw new UnsupportedOperationException(
        "hash(value) is not supported on the base Bucket class");
  }

  /**
   * Transforms a value to its corresponding partition value.
   *
   * @param value a source value
   * @return a transformed partition value
   * @deprecated will be removed in 2.0.0; use {@link #bind(Type)} instead
   */
  @Deprecated
  @Override
  public Integer apply(T value) {
    if (value == null) {
      return null;
    }
    return (hash(value) & Integer.MAX_VALUE) % numBuckets;
  }

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Call bucketTransform.bind(type) and use the returned SerializableFunction to hash/apply values.
  2. Subclass Bucket for a concrete type and override hash() if you need custom behavior.
  3. Refactor code to never invoke the protected hash() on the base type.

Example fix

// before
Bucket<String> bucket = (Bucket<String>) Bucket.get(Types.StringType.get(), 8);
int h = bucket.hash("abc");
// after
SerializableFunction<String, Integer> fn =
    Bucket.get(Types.StringType.get(), 8).bind(Types.StringType.get());
int h = fn.apply("abc");
Defensive patterns

Strategy: type-guard

Type guard

if (fn instanceof Bucket && fn.getClass() == Bucket.class) { throw new IllegalStateException("Bucket is not bound; call bind(type) first"); }

Try / catch

try { return boundFn.apply(value); } catch (UnsupportedOperationException e) { throw new IllegalStateException("Use the function returned by bind(type), not the base Bucket", e); }

Prevention

When it happens

Trigger: Invoking hash() directly on a raw Bucket<T> instance instead of the SerializableFunction returned by Bucket.bind(type); calling apply on an unbound generic Bucket.

Common situations: Custom transform/visitor code extending or instantiating Bucket directly; reflection-based utilities reaching into the base class; tests exercising hash paths (testTimestampNanoPromotion, testTimestampTzNanoPromotion) on the base class.

Related errors


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