apache/iceberg · error · IllegalArgumentException

Invalid primitive type for decimal

Error message

Invalid primitive type for decimal: <primitiveType>

What it means

ParquetValueReaders.bigDecimals builds a decimal reader based on the column's underlying Parquet primitive type. Only FIXED_LEN_BYTE_ARRAY, INT64, and INT32 physical types are valid for decimals; anything else (e.g. BINARY, FLOAT, DOUBLE, INT96) triggers IllegalArgumentException. It reflects a schema mismatch between the Parquet file's declared type and Iceberg's decimal expectation.

Solutions

  1. Fix the writer to declare decimals with the correct physical type (FIXED_LEN_BYTE_ARRAY, INT64, or INT32 per precision).
  2. Regenerate or rewrite the offending Parquet files with a correct decimal schema.
  3. Verify the file schema with parquet-tools and ensure the DECIMAL logical type annotation matches the physical type.
  4. If reading third-party data, map the column to a supported type instead of decimal.

Example fix

// before
Types.DecimalType.of(10, 2) written as parquet BINARY  // invalid
// after
// write with REQUIRED FIXED_LEN_BYTE_ARRAY(16) + DECIMAL(10,2) annotation,
// or INT64 for precision <= 18
Defensive patterns

Strategy: validation

Validate before calling

PrimitiveType t = desc.getPrimitiveType();
if (t.getPrimitiveTypeName() != FIXED_LEN_BYTE_ARRAY
    && t.getPrimitiveTypeName() != INT64
    && t.getPrimitiveTypeName() != INT32) {
  throw new IllegalArgumentException("Column cannot be read as decimal: " + t);
}

Try / catch

try {
  return ParquetValueReaders.bigDecimals(desc, scale);
} catch (IllegalArgumentException e) {
  LOG.error("Bad physical type for decimal: {}", desc.getPrimitiveType());
  throw e;
}

Prevention

When it happens

Trigger: Calling ParquetValueReaders.bigDecimals(ColumnDescriptor, int) with a descriptor whose primitive type is not FIXED_LEN_BYTE_ARRAY/INT64/INT32 — typically a file whose decimal column was written with an invalid physical type or whose schema annotation was applied to the wrong physical type.

Common situations: Files written by non-Iceberg or buggy writers declaring decimal as BINARY/other; schema evolution or hand-edited Parquet schemas applying DECIMAL logical type to float/double columns; mismatch between expected and actual file schema in a read task.

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/8a51f9f421789421. Report an issue: GitHub.

Appendix: source

Thrown at parquet/src/main/java/org/apache/iceberg/parquet/ParquetValueReaders.java:119

  public static ParquetValueReader<BigDecimal> bigDecimals(ColumnDescriptor desc) {
    LogicalTypeAnnotation decimal = desc.getPrimitiveType().getLogicalTypeAnnotation();
    Preconditions.checkArgument(
        decimal instanceof DecimalLogicalTypeAnnotation,
        "Invalid timestamp logical type: " + decimal);

    int scale = ((DecimalLogicalTypeAnnotation) decimal).getScale();

    switch (desc.getPrimitiveType().getPrimitiveTypeName()) {
      case FIXED_LEN_BYTE_ARRAY:
      case BINARY:
        return new BinaryAsDecimalReader(desc, scale);
      case INT64:
        return new LongAsDecimalReader(desc, scale);
      case INT32:
        return new IntegerAsDecimalReader(desc, scale);
    }

    throw new IllegalArgumentException(
        "Invalid primitive type for decimal: " + desc.getPrimitiveType());
  }

  public static ParquetValueReader<Long> times(ColumnDescriptor desc) {
    LogicalTypeAnnotation time = desc.getPrimitiveType().getLogicalTypeAnnotation();
    Preconditions.checkArgument(
        time instanceof TimeLogicalTypeAnnotation, "Invalid time logical type: " + time);

    TimeUnit unit = ((TimeLogicalTypeAnnotation) time).getUnit();
    if (unit == LogicalTypeAnnotation.TimeUnit.MILLIS) {
      return new TimeMillisReader(desc);
    }

    return new UnboxedReader<>(desc);
  }

  public static ParquetValueReader<Long> timestamps(ColumnDescriptor desc) {
    if (desc.getPrimitiveType().getPrimitiveTypeName() == PrimitiveType.PrimitiveTypeName.INT96) {

View on GitHub (pinned to 86d9c8fc54)