apache/iceberg · error · IllegalArgumentException

Unhandled type

Error message

Unhandled type 

What it means

SparkOrcWriter.primitive() maps Iceberg primitive types to ORC value writers. If it encounters an Iceberg primitive type with no ORC writer mapping (anything not covered by the switch cases, e.g. types invalid for ORC storage), it throws IllegalArgumentException("Unhandled type ..."). This fails writer creation for the whole file, before any rows are written.

Solutions

  1. Check the offending Iceberg type in the table schema (spark printSchema / metadata) and convert it to an ORC-supported type
  2. Recreate or alter the table with an ORC-compatible column type (e.g. use string/long instead of the unsupported type)
  3. If the type is a newly added Iceberg spec type, upgrade Iceberg to a version whose Spark writer supports it
  4. Cast unsupported columns before writing (e.g. df.withColumn("c", col("c").cast("string")))

Example fix

// before
df.writeTo("db.table_unsupported_type").append();
// after
Dataset<Row> fixed = df.withColumn("weird", functions.col("weird").cast("string"));
fixed.writeTo("db.table").append();
Defensive patterns

Strategy: validation

Validate before calling

for (Types.NestedField f : table.schema().columns()) {
  if (f.type().isPrimitiveType() && !ORC_SUPPORTED_TYPES.contains(f.type().asPrimitiveType().typeId())) {
    throw new IllegalStateException("Column " + f.name() + " has ORC-unsupported type " + f.type());
  }
}

Type guard

boolean isOrcWritable(Type type) {
  switch (type.typeId()) {
    case BOOLEAN: case INT: case LONG: case FLOAT: case DOUBLE: case DATE:
    case TIME: case TIMESTAMP: case STRING: case UUID: case FIXED:
    case BINARY: case DECIMAL: return true;
    default: return false;
  }
}

Try / catch

try {
  df.writeTo("db.tbl").append();
} catch (IllegalArgumentException e) {
  if (e.getMessage().startsWith("Unhandled type")) {
    // recast offending columns to ORC-supported types and retry
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Writing a Spark DataFrame via the Iceberg ORC writer when the table schema contains an Iceberg primitive type not handled by the switch (e.g. Iceberg types that ORC cannot represent or that newer spec versions added without writer support in this Spark 4.0 branch).

Common situations: Migrating tables between formats and landing a type ORC does not support (e.g. UUID or unknown future types in this branch); using a table whose schema was created with a newer Iceberg spec than this writer supports; accidental misuse of nested/binary types mapped incorrectly to ORC.

Related errors


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

Appendix: source

Thrown at spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/data/SparkOrcWriter.java:130

          return GenericOrcWriters.floats(ORCSchemaUtil.fieldId(primitive));
        case DOUBLE:
          return GenericOrcWriters.doubles(ORCSchemaUtil.fieldId(primitive));
        case BINARY:
          if (Type.TypeID.UUID == iPrimitive.typeId()) {
            return SparkOrcValueWriters.uuids();
          }
          return GenericOrcWriters.byteArrays();
        case STRING:
        case CHAR:
        case VARCHAR:
          return SparkOrcValueWriters.strings();
        case DECIMAL:
          return SparkOrcValueWriters.decimal(primitive.getPrecision(), primitive.getScale());
        case TIMESTAMP_INSTANT:
        case TIMESTAMP:
          return SparkOrcValueWriters.timestampTz();
        default:
          throw new IllegalArgumentException("Unhandled type " + primitive);
      }
    }
  }

  private static class InternalRowWriter extends GenericOrcWriters.StructWriter<InternalRow> {
    private final List<FieldGetter<?>> fieldGetters;

    InternalRowWriter(
        List<OrcValueWriter<?>> writers, Types.StructType iStruct, List<TypeDescription> orcTypes) {
      super(iStruct, writers);
      this.fieldGetters = Lists.newArrayListWithExpectedSize(orcTypes.size());

      Map<Integer, TypeDescription> idToType =
          orcTypes.stream().collect(Collectors.toMap(ORCSchemaUtil::fieldId, s -> s));

      for (Types.NestedField iField : iStruct.fields()) {
        fieldGetters.add(createFieldGetter(idToType.get(iField.fieldId())));
      }

View on GitHub (pinned to 86d9c8fc54)