apache/iceberg · error · UnsupportedOperationException
Unknown primitive physical type
Error message
Unknown primitive physical type: ${primitiveType} What it means
Thrown by VariantShreddingAnalyzer.createPrimitiveTypedValue() when it encounters a Parquet primitive physical type it cannot translate into a typed_value schema node while analyzing a shredding schema. This indicates the analyzer was handed a physical type outside the set it supports for Variant typed values.
Solutions
- Check which PrimitiveTypeName you pass into the analyzer and map it to a supported one for Variant typed values (e.g. BOOLEAN, INT32/64, FLOAT/DOUBLE, BINARY, FIXED_LEN_BYTE_ARRAY with UUID logical type).
- If you need the type for a valid Variant value, convert it before analysis (e.g. wrap fixed bytes with uuidType()).
- Upgrade Iceberg if the physical type is newly supported; otherwise open an issue to add a case for it.
Example fix
// before analyzer.buildTypedValue(PrimitiveTypeName.INT96) // after analyzer.buildTypedValue(PrimitiveTypeName.INT64) // use a supported primitive for typed_value
Defensive patterns
Strategy: validation
Validate before calling
switch (primitiveType) {
case BOOLEAN, INT32, INT64, FLOAT, DOUBLE, BINARY, FIXED_LEN_BYTE_ARRAY -> ok();
default -> throw new IllegalArgumentException("Unsupported for typed_value: " + primitiveType);
} Type guard
boolean isAnalyzablePrimitive(PrimitiveTypeName p) {
return switch (p) {
case BOOLEAN, INT32, INT64, FLOAT, DOUBLE, BINARY, FIXED_LEN_BYTE_ARRAY -> true;
default -> false;
};
} Try / catch
try {
analyzer.buildTypedValue(primitiveType);
} catch (UnsupportedOperationException e) {
log.warn("Falling back to unshredded variant: {}", e.getMessage());
useUnshreddedSchema();
} Prevention
- Feed the analyzer Iceberg Types-derived primitives rather than raw engine types
- Handle INT96 and unknown primitives explicitly before analysis
- Keep the Iceberg version current so new primitives get analyzer support
When it happens
Trigger: createPrimitiveTypedValue() called with a primitiveType that matches no case in its switch (e.g. an unexpected primitive reached the analyzer's typed-value construction via buildTypedValue).
Common situations: Programmatic shredding-spec construction passing an unmapped PrimitiveTypeName; a new Parquet primitive added upstream but not yet handled; internal misuse of the analyzer API with raw primitives instead of Iceberg Types.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Invalid bit width for int
- Invalid bit width for int
- Invalid primitive type for decimal
- Invalid primitive type for decimal
- Unsupported shredded value type
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/bdcf9d5703df3d4b.
Report an issue: GitHub.
Appendix: source
Thrown at parquet/src/main/java/org/apache/iceberg/parquet/VariantShreddingAnalyzer.java:421
Types.optional(PrimitiveType.PrimitiveTypeName.INT64)
.as(LogicalTypeAnnotation.timestampType(false, LogicalTypeAnnotation.TimeUnit.MICROS))
.named(TYPED_VALUE);
case TIMESTAMPTZ_NANOS ->
Types.optional(PrimitiveType.PrimitiveTypeName.INT64)
.as(LogicalTypeAnnotation.timestampType(true, LogicalTypeAnnotation.TimeUnit.NANOS))
.named(TYPED_VALUE);
case TIMESTAMPNTZ_NANOS ->
Types.optional(PrimitiveType.PrimitiveTypeName.INT64)
.as(LogicalTypeAnnotation.timestampType(false, LogicalTypeAnnotation.TimeUnit.NANOS))
.named(TYPED_VALUE);
case DECIMAL4, DECIMAL8, DECIMAL16 -> createDecimalTypedValue(info);
case UUID ->
Types.optional(PrimitiveType.PrimitiveTypeName.FIXED_LEN_BYTE_ARRAY)
.length(16)
.as(LogicalTypeAnnotation.uuidType())
.named(TYPED_VALUE);
default ->
throw new UnsupportedOperationException(
"Unknown primitive physical type: " + primitiveType);
};
}
/** Tracks occurrence count and types for a single field. */
private static class FieldInfo {
private static final PhysicalType[] PHYSICAL_TYPES = PhysicalType.values();
private static final List<PhysicalType> INTEGER_TYPES =
List.of(PhysicalType.INT8, PhysicalType.INT16, PhysicalType.INT32, PhysicalType.INT64);
private static final List<PhysicalType> DECIMAL_TYPES =
List.of(PhysicalType.DECIMAL4, PhysicalType.DECIMAL8, PhysicalType.DECIMAL16);
private final int[] typeCounts = new int[PHYSICAL_TYPES.length];
private int maxDecimalScale = 0;
private int maxDecimalIntegerDigits = 0;
private int observationCount = 0;View on GitHub (pinned to 86d9c8fc54)