apache/iceberg · error · IllegalStateException

Unknown type for binary field. Type name: " + bytes.getClass

Error message

Unknown type for binary field. Type name: " + bytes.getClass().getName()

What it means

StructInternalRow.getBinaryInternal materializes a Spark BINARY column from the underlying field, which Iceberg stores as either ByteBuffer or byte[]. Any other Java class means the stored object doesn't match the declared binary type, so it throws IllegalStateException with the actual class name.

Source

Thrown at spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/source/StructInternalRow.java:201

    CharSequence seq = struct.get(ordinal, CharSequence.class);
    return UTF8String.fromString(seq.toString());
  }

  @Override
  public byte[] getBinary(int ordinal) {
    return isNullAt(ordinal) ? null : getBinaryInternal(ordinal);
  }

  private byte[] getBinaryInternal(int ordinal) {
    Object bytes = struct.get(ordinal, Object.class);

    // should only be either ByteBuffer or byte[]
    if (bytes instanceof ByteBuffer) {
      return ByteBuffers.toByteArray((ByteBuffer) bytes);
    } else if (bytes instanceof byte[]) {
      return (byte[]) bytes;
    } else {
      throw new IllegalStateException(
          "Unknown type for binary field. Type name: " + bytes.getClass().getName());
    }
  }

  @Override
  public CalendarInterval getInterval(int ordinal) {
    throw new UnsupportedOperationException("Unsupported type: interval");
  }

  @Override
  public InternalRow getStruct(int ordinal, int numFields) {
    return isNullAt(ordinal) ? null : getStructInternal(ordinal);
  }

  private InternalRow getStructInternal(int ordinal) {
    return new StructInternalRow(
        type.fields().get(ordinal).type().asStructType(), struct.get(ordinal, StructLike.class));
  }

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Ensure the producer stores ByteBuffer or byte[] for binary fields
  2. Check the class name in the message and convert at the producer (e.g. wrap byte[] in ByteBuffer or call array())
  3. Verify the expected Spark schema matches the table's Iceberg type (BinaryType vs StringType mixups are common)
  4. Upgrade/rebuild against the same Iceberg version used to write, so Java class mappings agree

Example fix

// before
byte[] b = row.getBinaryInternal(ordinal); // stored String -> IllegalStateException
// after
// fix producer:
struct.set(ordinal, ByteBuffer.wrap(stringValue.getBytes(StandardCharsets.UTF_8)));
Defensive patterns

Strategy: type-guard

Validate before calling

Object v = struct.get(pos, javaClass);
if (!(v instanceof ByteBuffer) && !(v instanceof byte[])) {
  throw new IllegalStateException("expected ByteBuffer or byte[] for binary field, got " + v.getClass());
}

Type guard

boolean isBinaryBacked(Object v) { return v instanceof ByteBuffer || v instanceof byte[]; }

Try / catch

try { byte[] b = row.getBinaryInternal(ordinal); } catch (IllegalStateException e) { /* convert or fix the producer to store ByteBuffer/byte[] */ }

Prevention

When it happens

Trigger: Reading a column declared as Spark BinaryType while the Iceberg struct actually contains e.g. String, ByteString, or a heap ByteBuffer subtype that failed boxing expectations — a schema/value type mismatch between producer and reader.

Common situations: Custom row wrappers storing non-standard types in the struct; schema drift where the field changed type after being written; wrong expected schema in a custom scan.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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