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

  1. Always call source.split(desiredNumSplits, options) and create readers from the returned sub-sources, not from the original source
  2. If split() returns an empty list, treat the source as empty instead of reading it directly
  3. 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

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


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)