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

  1. 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).
  2. If you need the type for a valid Variant value, convert it before analysis (e.g. wrap fixed bytes with uuidType()).
  3. 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

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


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)