apache/iceberg · error · IllegalArgumentException

Invalid primitive type for decimal:

Error message

Invalid primitive type for decimal: 

What it means

ValueReaders.decimalBytesReader supports decimals stored as Avro FIXED or BYTES; any other Avro primitive type cannot carry decimal bytes and triggers this IllegalArgumentException. It guards the schema used for the logical decimal type.

Source

Thrown at core/src/main/java/org/apache/iceberg/avro/ValueReaders.java:142

    return BytesReader.INSTANCE;
  }

  public static ValueReader<ByteBuffer> byteBuffers() {
    return ByteBufferReader.INSTANCE;
  }

  public static ValueReader<BigDecimal> decimal(ValueReader<byte[]> unscaledReader, int scale) {
    return new DecimalReader(unscaledReader, scale);
  }

  public static ValueReader<byte[]> decimalBytesReader(Schema schema) {
    switch (schema.getType()) {
      case FIXED:
        return ValueReaders.fixed(schema.getFixedSize());
      case BYTES:
        return ValueReaders.bytes();
      default:
        throw new IllegalArgumentException(
            "Invalid primitive type for decimal: " + schema.getType());
    }
  }

  public static ValueReader<Variant> variants() {
    return VariantReader.INSTANCE;
  }

  public static ValueReader<Object> union(List<ValueReader<?>> readers) {
    return new UnionReader(readers);
  }

  public static ValueReader<Long> positions() {
    return new PositionReader();
  }

  public static <T> ValueReader<Collection<T>> array(ValueReader<T> elementReader) {
    return new ArrayReader<>(elementReader);

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Fix the Avro schema so the decimal field's type is bytes with logicalType 'decimal' or fixed with the required byte size
  2. Compute the required bytes with TypeUtil.decimalRequiredBytes(precision) when using fixed
  3. Rewrite the file with a writer that follows the Iceberg Avro encoding for decimals

Example fix

// before
{"type": "string", "logicalType": "decimal", "precision": 10, "scale": 2}
// after
{"type": "bytes", "logicalType": "decimal", "precision": 10, "scale": 2}
Defensive patterns

Strategy: validation

Validate before calling

Schema.Field f = schema.getField("amount");
Schema type = f.schema().getTypes().size() > 1 ? f.schema().getTypes().get(1) : f.schema();
boolean ok = type.getType() == Schema.Type.BYTES || type.getType() == Schema.Type.FIXED;

Type guard

boolean decimalSchemaOk(Schema s) { return s.getLogicalType() instanceof LogicalTypes.Decimal && (s.getType() == Schema.Type.BYTES || s.getType() == Schema.Type.FIXED); }

Try / catch

try { ValueReader<BigDecimal> r = ValueReaders.decimals(schema); } catch (IllegalArgumentException e) { if (e.getMessage().startsWith("Invalid primitive type for decimal")) { /* fix schema */ } else throw e; }

Prevention

When it happens

Trigger: Reading an Avro schema where a decimal logical type is attached to an unexpected primitive (e.g. STRING or INT) instead of FIXED (with fixed-size) or BYTES.

Common situations: Hand-written or tool-rewritten Avro schemas losing the underlying primitive type; foreign writers serializing decimals incorrectly; schema translation bugs.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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