apache/beam · error · UnsupportedOperationException
BigQuery storage source must be split before reading
Error message
BigQuery storage source must be split before reading
What it means
BigQueryStorageSourceBase.createReader requires the source to have been split into concrete stream sources first. If split() was never called (or the source is neither empty/pruned nor split), the source has no underlying streams and reading is unsupported, so an UnsupportedOperationException is thrown.
Solutions
- Always call source.split(desiredNumSplits, options) and create readers from the returned sub-sources, not from the original source
- If split() returns an empty list, treat the source as empty instead of reading it directly
- Check the split/mark logic in your runner or wrapper so emptyOrPruned is set before createReader is invoked
Example fix
// before
BigQueryStorageSourceBase<T> src = ...;
BoundedReader<T> r = src.createReader(options); // unsplit -> throws
// after
List<? extends BoundedSource<T>> splits = src.split(10, options);
if (splits.isEmpty()) { /* treat as empty input */ }
BoundedReader<T> r = splits.get(0).createReader(options); Defensive patterns
Strategy: validation
Validate before calling
if (!source.isSplit() /* or track your own flag */) {
throw new IllegalStateException("call split(numSplits, options) before createReader");
}
List<? extends BoundedSource<T>> splits = source.split(10, options);
if (splits.isEmpty()) { /* handle empty input instead of reading */ } Try / catch
try {
reader = subSource.createReader(options);
} catch (UnsupportedOperationException e) {
if (e.getMessage().contains("must be split before reading")) {
reader = source.split(1, options).get(0).createReader(options);
} else throw e;
} Prevention
- Always obtain readers from split sub-sources, never the original BigQueryStorageSourceBase
- Handle split() returning an empty list as an empty dataset, not a read target
- Test custom runners/wrappers against BoundedSource contract (split then createReader)
When it happens
Trigger: Creating a BoundedReader directly from a BigQueryStorageSourceBase without first calling split(numSplits, options); calling createReader when split returned zero streams and the source was not marked emptyOrPruned; framework misuse when wrapping the unsplit source (e.g. UnboundedReadFromBoundedSource fallback paths).
Common situations: Custom runner or test harness reading the source manually and skipping split(); adapters that wrap BoundedSource and call createReader before split; bug in code that wraps the original unsplit source when split returns an empty list.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- BigQuery source must be split before being read
- createRowWriter called when dynamicDestinations is null…
- createRowWriter called when schemaFactory is null; forgot…
- getCoder is called before init
- Read session does not have Avro/Arrow schema set.
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/3007a88ba731485f.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryStorageSourceBase.java:234
List<BigQueryStorageStreamSource<T>> sources = Lists.newArrayList();
for (ReadStream readStream : readSession.getStreamsList()) {
sources.add(
BigQueryStorageStreamSource.create(
readSession, readStream, tableSchema, parseFn, outputCoder, bqServices));
}
return ImmutableList.copyOf(sources);
}
@Override
public BoundedReader<T> createReader(PipelineOptions options) throws IOException {
if (emptyOrPruned) {
// When split() returns an empty list, UnboundedReadFromBoundedSource falls back to wrapping
// the original unsplit source directly (ImmutableList.of(bigQuerySotrageSourceBase)) so we
// need to return empty reader.
return new EmptyReader<>(this);
}
throw new UnsupportedOperationException("BigQuery storage source must be split before reading");
}
private static class EmptyReader<T> extends BoundedReader<T> {
private final BigQueryStorageSourceBase<T> source;
EmptyReader(BigQueryStorageSourceBase<T> source) {
this.source = source;
}
@Override
public boolean start() throws IOException {
return false;
}
@Override
public boolean advance() throws IOException {
return false;
}View on GitHub (pinned to 12126d8942)