{"record":{"id":"3007a88ba731485f","repo":"apache/beam","slug":"bigquery-storage-source-must-be-split-before-reading","errorCode":null,"errorMessage":"BigQuery storage source must be split before reading","messagePattern":"BigQuery storage source must be split before reading","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryStorageSourceBase.java","lineNumber":234,"sourceCode":"    List<BigQueryStorageStreamSource<T>> sources = Lists.newArrayList();\n    for (ReadStream readStream : readSession.getStreamsList()) {\n      sources.add(\n          BigQueryStorageStreamSource.create(\n              readSession, readStream, tableSchema, parseFn, outputCoder, bqServices));\n    }\n\n    return ImmutableList.copyOf(sources);\n  }\n\n  @Override\n  public BoundedReader<T> createReader(PipelineOptions options) throws IOException {\n    if (emptyOrPruned) {\n      // When split() returns an empty list, UnboundedReadFromBoundedSource falls back to wrapping\n      // the original unsplit source directly (ImmutableList.of(bigQuerySotrageSourceBase)) so we\n      // need to return empty reader.\n      return new EmptyReader<>(this);\n    }\n    throw new UnsupportedOperationException(\"BigQuery storage source must be split before reading\");\n  }\n\n  private static class EmptyReader<T> extends BoundedReader<T> {\n    private final BigQueryStorageSourceBase<T> source;\n\n    EmptyReader(BigQueryStorageSourceBase<T> source) {\n      this.source = source;\n    }\n\n    @Override\n    public boolean start() throws IOException {\n      return false;\n    }\n\n    @Override\n    public boolean advance() throws IOException {\n      return false;\n    }","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryStorageSourceBase.java#L216-L252","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\nBigQueryStorageSourceBase<T> src = ...;\nBoundedReader<T> r = src.createReader(options); // unsplit -> throws\n// after\nList<? extends BoundedSource<T>> splits = src.split(10, options);\nif (splits.isEmpty()) { /* treat as empty input */ }\nBoundedReader<T> r = splits.get(0).createReader(options);","handlingStrategy":"validation","validationCode":"if (!source.isSplit() /* or track your own flag */) {\n  throw new IllegalStateException(\"call split(numSplits, options) before createReader\");\n}\nList<? extends BoundedSource<T>> splits = source.split(10, options);\nif (splits.isEmpty()) { /* handle empty input instead of reading */ }","typeGuard":null,"tryCatchPattern":"try {\n  reader = subSource.createReader(options);\n} catch (UnsupportedOperationException e) {\n  if (e.getMessage().contains(\"must be split before reading\")) {\n    reader = source.split(1, options).get(0).createReader(options);\n  } else throw e;\n}","preventionTips":["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)"],"tags":["bigquery","storage-read-api","lifecycle","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}