apache/beam · error · RuntimeException
Unexpected null schema!
Error message
Unexpected null schema!
What it means
fieldDescriptorFromBeamField, when handling a ROW-typed Beam field, calls field.getType().getRowSchema() and throws "Unexpected null schema!" if it returns null. A ROW FieldType must always carry a nested Schema; a null result means the field type was constructed without one, so nested TableFieldSchema fields can't be derived.
Solutions
- Attach the nested schema: Schema.FieldType.row(nestedSchema) instead of a bare ROW type
- Verify every ROW field in your Schema has a non-null row schema before writing
- Check the code path that constructs your Schema (e.g. from a POJO) to ensure nested objects are registered
- Rebuild nested fields with Schema.builder().add... to guarantee a complete schema
Example fix
// before Schema.FieldType rowType = Schema.FieldType.row(null); // or bare ROW // after Schema.FieldType rowType = Schema.FieldType.row(nestedSchema);
Defensive patterns
Strategy: validation
Validate before calling
// Java
for (Schema.Field f : beamSchema.getFields()) {
if (f.getType().getTypeName() == TypeName.ROW && f.getType().getRowSchema() == null) {
throw new IllegalStateException("ROW field without nested schema: " + f.getName());
}
} Type guard
// Java
Schema nested = fieldType.getRowSchema();
if (nested != null) { /* safe to recurse */ } Try / catch
// Java
try {
descriptor = BeamRowToStorageApiProto.protoTableSchemaFromBeamSchema(beamSchema);
} catch (RuntimeException e) {
if (e.getMessage().equals("Unexpected null schema!")) {
// rebuild the schema attaching nested row schemas
}
throw e;
} Prevention
- Always construct ROW fields via Schema.FieldType.row(nestedSchema)
- Validate nested schemas recursively before pipeline submission
- Avoid hand-building FieldTypes with raw TypeName values
When it happens
Trigger: Building a Beam Schema field with Schema.FieldType.row(null) or a FieldType incorrectly typed as ROW without attaching the nested schema, then writing via BigQueryIO Storage API
Common situations: Reflection/inference-based schema builders producing ROW fields without schemas; custom FieldType factories; deserialized schemas missing nested definitions.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Cannot cast to a compatible object to build ByteString.
- Reserved field name " + field.getName() + " in user schema.
- Unexpected null element type!
- Unexpected null element type on " + field.getName()
- Unexpected null logical type " + field.getType()
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/fc73f793a385520f.
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:214
TableSchema.Builder builder = TableSchema.newBuilder();
for (Field field : schema.getFields()) {
builder.addFields(fieldDescriptorFromBeamField(field));
}
return builder.build();
}
private static TableFieldSchema fieldDescriptorFromBeamField(Field field) {
TableFieldSchema.Builder builder = TableFieldSchema.newBuilder();
if (StorageApiCDC.COLUMNS.contains(field.getName())) {
throw new RuntimeException("Reserved field name " + field.getName() + " in user schema.");
}
builder = builder.setName(field.getName().toLowerCase());
switch (field.getType().getTypeName()) {
case ROW:
@Nullable Schema rowSchema = field.getType().getRowSchema();
if (rowSchema == null) {
throw new RuntimeException("Unexpected null schema!");
}
builder = builder.setType(TableFieldSchema.Type.STRUCT);
for (Schema.Field nestedField : rowSchema.getFields()) {
builder = builder.addFields(fieldDescriptorFromBeamField(nestedField));
}
break;
case ARRAY:
case ITERABLE:
@Nullable FieldType elementType = field.getType().getCollectionElementType();
if (elementType == null) {
throw new RuntimeException("Unexpected null element type on " + field.getName());
}
TypeName containedTypeName =
Preconditions.checkNotNull(
elementType.getTypeName(),
"Null type name found in contained type at %s",
field.getName());
Preconditions.checkState(View on GitHub (pinned to 12126d8942)