apache/beam · error · IllegalArgumentException
Schema can't be empty
Error message
Schema can't be empty
What it means
When a table read is configured (no SQL), getReadOperation() uses the schema's field names as the columns to fetch. It throws IllegalArgumentException("Schema can't be empty") when the schema equals an empty Schema.builder().build(), i.e. no fields were defined — a table read without columns is meaningless for the connector.
Source
Thrown at sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/spanner/SpannerTransformRegistrar.java:232
return mode == READ_TIMESTAMP
? TimestampBound.ofReadTimestamp(Timestamp.parseTimestamp(readTimestamp))
: TimestampBound.ofMinReadTimestamp(Timestamp.parseTimestamp(readTimestamp));
default:
throw new IllegalArgumentException("Unknown timestamp bound mode: " + mode);
}
}
public ReadOperation getReadOperation() {
if (sql != null && table != null) {
throw new IllegalStateException(
"Query and table params are mutually exclusive. Set just one of them.");
}
ReadOperation readOperation = ReadOperation.create();
if (sql != null) {
return readOperation.withQuery(sql);
}
if (Schema.builder().build().equals(schema)) {
throw new IllegalArgumentException("Schema can't be empty");
}
if (table != null) {
return readOperation.withTable(table).withColumns(schema.getFieldNames());
}
throw new IllegalStateException("Can't happen");
}
}
@Override
@NonNull
public PTransform<PBegin, PCollection<Row>> buildExternal(
ReadBuilder.Configuration configuration) {
configuration.checkMandatoryFields();
SpannerIO.Read readTransform =
SpannerIO.read()
.withProjectId(configuration.projectId)
.withDatabaseId(configuration.databaseId)View on GitHub (pinned to 12126d8942)
Solutions
- Add .withSchema(Schema.builder().addInt64Field("id").addStringField("name").build()) listing the columns you want.
- Alternatively use .withQuery("SELECT * FROM table") if you want all columns without declaring a schema.
- Ensure the columns in the schema match actual Spanner table column names (withColumns uses schema.getFieldNames()).
Example fix
// before
SpannerIO.read().withInstanceId("i").withDatabaseId("db").withTable("users")
// after
SpannerIO.read().withTable("users").withSchema(Schema.builder().addInt64Field("id").addStringField("name").build()) Defensive patterns
Strategy: validation
Validate before calling
if (sql == null && (schema == null || schema.getFields().isEmpty())) { throw new IllegalArgumentException("table reads require a non-empty schema listing columns"); } Try / catch
try { ReadOperation op = config.getReadOperation(); } catch (IllegalArgumentException e) { throw new ConfigException("Spanner table read needs columns: " + e.getMessage(), e); } Prevention
- Always declare the columns you need via Schema when reading a table.
- Switch to withQuery("SELECT * FROM table") when you truly want all columns.
- Validate schema field names against the live table schema during pipeline setup.
When it happens
Trigger: Calling SpannerIO.read().withTable("my_table") without .withSchema(...) or with an empty schema, or building the schema from a config/JSON list of columns that was empty.
Common situations: Users assuming the connector will auto-discover all columns when reading a table (it requires an explicit column list via schema); deserialized schemas where fields array was empty; migration from query-based reads where schema was optional.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Row Schema does not contain the following specified fields:
- Row expected <fieldCount> fields (<fields>). initialized wit
- databaseId can't be empty
- instanceId can't be empty
- Both clientCertPath and clientCertKeyPath must be specified
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/d0f7e313ac189f73.
Report an issue: GitHub.