{"record":{"id":"9a8a1142d560aab2","repo":"apache/iceberg","slug":"hash-value-is-not-supported-on-the-base-bucket-cl","errorCode":null,"errorMessage":"hash(value) is not supported on the base Bucket class","messagePattern":"hash\\(value\\) is not supported on the base Bucket class","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/org/apache/iceberg/transforms/Bucket.java","lineNumber":99,"sourceCode":"\n  private final int numBuckets;\n\n  private Bucket(int numBuckets) {\n    this.numBuckets = numBuckets;\n  }\n\n  public Integer numBuckets() {\n    return numBuckets;\n  }\n\n  @Override\n  public SerializableFunction<T, Integer> bind(Type type) {\n    Preconditions.checkArgument(canTransform(type), \"Cannot bucket by type: %s\", type);\n    return get(type, numBuckets);\n  }\n\n  protected int hash(T value) {\n    throw new UnsupportedOperationException(\n        \"hash(value) is not supported on the base Bucket class\");\n  }\n\n  /**\n   * Transforms a value to its corresponding partition value.\n   *\n   * @param value a source value\n   * @return a transformed partition value\n   * @deprecated will be removed in 2.0.0; use {@link #bind(Type)} instead\n   */\n  @Deprecated\n  @Override\n  public Integer apply(T value) {\n    if (value == null) {\n      return null;\n    }\n    return (hash(value) & Integer.MAX_VALUE) % numBuckets;\n  }","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/api/src/main/java/org/apache/iceberg/transforms/Bucket.java#L81-L117","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Call bucketTransform.bind(type) and use the returned SerializableFunction to hash/apply values.","Subclass Bucket for a concrete type and override hash() if you need custom behavior.","Refactor code to never invoke the protected hash() on the base type."],"exampleFix":"// before\nBucket<String> bucket = (Bucket<String>) Bucket.get(Types.StringType.get(), 8);\nint h = bucket.hash(\"abc\");\n// after\nSerializableFunction<String, Integer> fn =\n    Bucket.get(Types.StringType.get(), 8).bind(Types.StringType.get());\nint h = fn.apply(\"abc\");","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"if (fn instanceof Bucket && fn.getClass() == Bucket.class) { throw new IllegalStateException(\"Bucket is not bound; call bind(type) first\"); }","tryCatchPattern":"try { return boundFn.apply(value); } catch (UnsupportedOperationException e) { throw new IllegalStateException(\"Use the function returned by bind(type), not the base Bucket\", e); }","preventionTips":["Always call bind(type) and use the returned SerializableFunction","Never invoke protected hash() from outside subclasses","Add tests that hash via the bound function for each type"],"tags":["java","transforms","bucketing","unsupported-operation"],"backgroundTag":"method-not-implemented","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}