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
- Fix the writer to declare decimals with the correct physical type (FIXED_LEN_BYTE_ARRAY, INT64, or INT32 per precision).
- Regenerate or rewrite the offending Parquet files with a correct decimal schema.
- Verify the file schema with parquet-tools and ensure the DECIMAL logical type annotation matches the physical type.
- 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
- Write decimals with FIXED_LEN_BYTE_ARRAY, INT64, or INT32 physical types.
- Validate file schemas against the Iceberg schema before reads.
- Check logical type annotations match physical types with parquet-tools.
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
- Buffer size of is larger than requested size of
- Invalid primitive type for decimal
- Invalid primitive type for decimal
- Unsupported base type for decimal:
- Unsupported base type for decimal:
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)