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

  1. Convert the value to byte[] before inserting into the Row: value.toString().getBytes(UTF_8) or ((ByteBuffer) v).array()
  2. Verify the field's declared Beam FieldType matches the value actually stored in the Row
  3. Use Schema.FieldType.BYTES consistently from the source that populates the row
  4. 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

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


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)