apache/beam · error · ClassCastException
Cannot cast to a compatible object to build ByteString.
Error message
Cannot cast %s to a compatible object to build ByteString.
What it means
BeamRowToStorageApiProto.toProtoByteString converts a Beam Row field value (BYTES-typed column) into a protobuf ByteString. It accepts byte[], ByteBuffer, and String; any other runtime type triggers ClassCastException. This means the Row's declared BYTES field actually holds an incompatible object.
Solutions
- Convert the value to byte[] before inserting into the Row: value.toString().getBytes(UTF_8) or ((ByteBuffer) v).array()
- Verify the field's declared Beam FieldType matches the value actually stored in the Row
- Use Schema.FieldType.BYTES consistently from the source that populates the row
- Log o.getClass() at the failing site to identify the actual runtime type
Example fix
// before row = Row.withSchema(schema).addValues(someString).build(); // BYTES column // after row = Row.withSchema(schema).addValues(someString.getBytes(StandardCharsets.UTF_8)).build();
Defensive patterns
Strategy: type-guard
Validate before calling
// Java
if (!(v instanceof byte[] || v instanceof ByteBuffer || v instanceof String)) {
throw new IllegalStateException("BYTES column value must be byte[]/ByteBuffer/String, got " + v.getClass());
} Type guard
// Java
static ByteString toByteStringSafe(Object o) {
if (o instanceof byte[]) return ByteString.copyFrom((byte[]) o);
if (o instanceof ByteBuffer) return ByteString.copyFrom((ByteBuffer) o);
if (o instanceof String) return ByteString.copyFromUtf8((String) o);
return null; // caller checks
} Try / catch
// Java
try {
proto = BeamRowToStorageApiProto.messageFromBeamRow(...);
} catch (ClassCastException e) {
if (e.getMessage().contains("compatible object to build ByteString")) {
// coerce the offending field to byte[] before writing
}
throw e;
} Prevention
- Populate BYTES fields only with byte[] (or ByteBuffer/String)
- Assert row value types match the declared Schema in tests
- Convert strings to UTF-8 bytes explicitly at the row-construction site
When it happens
Trigger: Building a Beam Row where a BYTES field is set with a non-byte value (e.g. an int, a List<Byte>, or a custom object) and then writing to BigQuery via Storage API
Common situations: Rows constructed manually with row.getBytes(i) mismatches, values deserialized from another format retaining exotic types, or schema changes where a column switched from STRING to BYTES but values weren't converted.
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
- Reserved field name " + field.getName() + " in user schema.
- Unexpected null element type on " + field.getName()
- Unexpected null logical type " + field.getType()
- Unexpected null schema!
- A function must be provided to convert the input type into…
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/2e1fc875c08539be.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BeamRowToStorageApiProto.java:120
.put(TypeName.FLOAT, o -> Double.valueOf(o.toString()))
.put(TypeName.DOUBLE, Function.identity())
.put(TypeName.STRING, Function.identity())
.put(TypeName.BOOLEAN, Function.identity())
// A Beam DATETIME is actually a timestamp, not a DateTime.
.put(TypeName.DATETIME, o -> ((ReadableInstant) o).getMillis() * 1000)
.put(TypeName.BYTES, BeamRowToStorageApiProto::toProtoByteString)
.put(TypeName.DECIMAL, o -> serializeBigDecimalToNumeric((BigDecimal) o))
.build();
private static ByteString toProtoByteString(Object o) {
if (o instanceof byte[]) {
return ByteString.copyFrom((byte[]) o);
} else if (o instanceof ByteBuffer) {
return ByteString.copyFrom((ByteBuffer) o);
} else if (o instanceof String) {
return ByteString.copyFromUtf8((String) o);
} else {
throw new ClassCastException(
String.format(
"Cannot cast %s to a compatible object to build ByteString.", o.getClass()));
}
}
// A map of supported logical types to their encoding functions.
static final Map<String, BiFunction<LogicalType<?, ?>, Object, Object>> LOGICAL_TYPE_ENCODERS =
ImmutableMap.<String, BiFunction<LogicalType<?, ?>, Object, Object>>builder()
.put(
SqlTypes.DATE.getIdentifier(),
(logicalType, value) -> (int) ((LocalDate) value).toEpochDay())
.put(
SqlTypes.TIME.getIdentifier(),
(logicalType, value) -> CivilTimeEncoder.encodePacked64TimeMicros((LocalTime) value))
.put(
SqlTypes.DATETIME.getIdentifier(),
(logicalType, value) ->
CivilTimeEncoder.encodePacked64DatetimeMicros((LocalDateTime) value))View on GitHub (pinned to 12126d8942)