apache/iceberg · error · IllegalStateException
Unknown type for binary field. Type name: +…
Error message
Unknown type for binary field. Type name: + bytes.getClass().getName()
What it means
StructInternalRow.getBinaryInternal wraps the underlying value of an Iceberg binary field for Spark and expects it to be either a ByteBuffer or a byte[]. If the stored value is any other Java type, it cannot be converted to a byte array, so an IllegalStateException is thrown. This is an internal invariant violation: the in-memory value does not match the declared binary column type.
Solutions
- Inspect the actual class name in the message and fix the producer so it stores byte[] or ByteBuffer for binary fields.
- Convert the value before building the row: ByteBuffers.toByteArray(buffer) for buffers, or an explicit encoding (e.g. String.getBytes(UTF_8)) for other types.
- Check for an intermediate transform (custom InternalRow wrapper or Spark version migration) changing the stored representation.
- Re-read the same rows with a vanilla Spark Iceberg reader to determine whether the source data or your code path is at fault.
Example fix
// before
row.set(ordinal, someString); // arbitrary object in a binary column
// after
if (!(value instanceof ByteBuffer) && !(value instanceof byte[])) {
throw new IllegalArgumentException("Expected ByteBuffer or byte[], got " + value.getClass());
}
row.set(ordinal, value); Defensive patterns
Strategy: type-guard
Validate before calling
Object v = row.get(ordinal);
if (v != null && !(v instanceof ByteBuffer) && !(v instanceof byte[])) {
throw new IllegalStateException("Unexpected binary representation: " + v.getClass());
} Type guard
static boolean isBinaryRepr(Object v) {
return v == null || v instanceof ByteBuffer || v instanceof byte[];
} Try / catch
try {
byte[] b = structRow.getBinary(ordinal);
} catch (IllegalStateException e) {
// parse class name from message, quarantine the record, re-encode from source
} Prevention
- Always build rows with byte[] or ByteBuffer for Iceberg BinaryType columns.
- Round-trip binary columns through StructInternalRow in integration tests.
- Centralize row construction in one factory so conversions are applied consistently.
When it happens
Trigger: Calling getBinary(ordinal) on a StructInternalRow whose backing value for that ordinal is neither ByteBuffer nor byte[] — e.g. custom data-source integrations, reflection-based row construction that bypassed the expected conversion, or upstream code that stored a String or wrapper object in a binary column.
Common situations: Custom Spark readers/writers feeding Iceberg rows with the wrong in-memory representation; regressions after a Spark or Iceberg upgrade where a producer changed its binary encoding; corrupt values from a misbehaving extension that mutated row data.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Cannot load underlying Iceberg view for view: $ident
- StructInternalRow is read-only
- StructInternalRow is read-only
- Unknown type for binary field. Type name:
- Unknown type for binary field. Type name: " +…
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/957e43816ae2f13c.
Report an issue: GitHub.
Appendix: source
Thrown at spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/source/StructInternalRow.java:199
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)