apache/iceberg · error · UnsupportedOperationException

Expected number of buckets to be tinyint, shortint or int

Error message

Expected number of buckets to be tinyint, shortint or int

What it means

Type validation in BucketFunction.bind: the first argument (number of buckets) is not one of byte/tinyint, short/smallint or int — the accepted types for the bucket count. The %s names the offending field; cast the bucket count to int before calling system.bucket.

Source

Thrown at spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/functions/BucketFunction.java:79

  private static final int NUM_BUCKETS_ORDINAL = 0;
  private static final int VALUE_ORDINAL = 1;

  private static final Set<DataType> SUPPORTED_NUM_BUCKETS_TYPES =
      ImmutableSet.of(DataTypes.ByteType, DataTypes.ShortType, DataTypes.IntegerType);

  @Override
  @SuppressWarnings("checkstyle:CyclomaticComplexity")
  public BoundFunction bind(StructType inputType) {
    if (inputType.size() != 2) {
      throw new UnsupportedOperationException(
          "Wrong number of inputs (expected numBuckets and value)");
    }

    StructField numBucketsField = inputType.fields()[NUM_BUCKETS_ORDINAL];
    StructField valueField = inputType.fields()[VALUE_ORDINAL];

    if (!SUPPORTED_NUM_BUCKETS_TYPES.contains(numBucketsField.dataType())) {
      throw new UnsupportedOperationException(
          "Expected number of buckets to be tinyint, shortint or int");
    }

    DataType type = valueField.dataType();
    if (type instanceof DateType) {
      return new BucketInt(type);
    } else if (type instanceof ByteType
        || type instanceof ShortType
        || type instanceof IntegerType) {
      return new BucketInt(DataTypes.IntegerType);
    } else if (type instanceof LongType) {
      return new BucketLong(type);
    } else if (type instanceof TimestampType) {
      return new BucketLong(type);
    } else if (type instanceof TimestampNTZType) {
      return new BucketLong(type);
    } else if (type instanceof DecimalType) {
      return new BucketDecimal(type);

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Use a tinyint/short/int value for numBuckets, e.g. system.bucket(8, id) with an int literal
  2. Cast the numBuckets column: system.bucket(CAST(n AS INT), id)
  3. Avoid passing bigint columns as bucket counts; compute with int

Example fix

// before
SELECT system.bucket(CAST(8 AS BIGINT), id) FROM t;
// after
SELECT system.bucket(8, id) FROM t;
Defensive patterns

Strategy: type-guard

Validate before calling

if (numBuckets.dataType() != IntegerType && numBuckets.dataType() != ShortType && numBuckets.dataType() != ByteType) {
  throw new IllegalArgumentException("numBuckets must be int/short/tinyint");
}

Type guard

boolean isIntLike(DataType t) {
  return t instanceof ByteType || t instanceof ShortType || t instanceof IntegerType;
}

Try / catch

try {
  bound = bucketFn.bind(inputType);
} catch (UnsupportedOperationException e) {
  throw new IllegalArgumentException("Cast numBuckets to INT", e);
}

Prevention

When it happens

Trigger: Invoking system.bucket(...) with numBuckets supplied as a long, decimal, string literal, or a non-integral column type.

Common situations: Passing a long constant like 8L in Scala/Java API; numBuckets read from a bigint column; schema evolution changing the bucket-count column type.

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/9c1bcb90ad8c2851. Report an issue: GitHub.