apache/beam · error · IllegalStateException
Firestore read transform does not expect input PCollections.
Error message
Firestore read transform does not expect input PCollections.
What it means
FirestoreReadSchemaTransformProvider.ReadTransform is a source transform: it produces output rows and therefore requires an empty PCollectionRowTuple input. expand() eagerly validates this and throws IllegalStateException if any input PCollections were passed in, since a read cannot consume elements.
Solutions
- Use the read transform only as a pipeline source with no input PCollections
- If you need Firestore data mid-pipeline, use FirestoreV1.read() transforms or restructure so the read emits into the downstream stage instead of receiving input
- Check your SchemaTransform composition/YAML so the read step has no 'input' references
Example fix
// before
PCollectionRowTuple.of("rows", existingRows).apply(firestoreReadTransform)
// after
PCollectionRowTuple.empty(pipeline).apply(firestoreReadTransform) Defensive patterns
Strategy: validation
Validate before calling
if (!input.getAll().isEmpty()) {
throw new IllegalArgumentException("firestore read transform must be a source: pass an empty PCollectionRowTuple");
} Type guard
boolean isSourceUsage(PCollectionRowTuple input) { return input.getAll().isEmpty(); } Try / catch
try {
PCollectionRowTuple result = input.apply(readTransform);
} catch (IllegalStateException e) {
LOG.error("Firestore read got inputs; restructure pipeline so it is the source", e);
} Prevention
- Read SchemaTransforms are sources — never connect them downstream of another transform
- In YAML pipelines keep the read step free of 'input' references
- Use FirestoreV1 read transforms for mid-pipeline reads
When it happens
Trigger: Wiring a Firestore read SchemaTransform into a pipeline where PCollectionRowTuple.input contains one or more named PCollections — e.g. composing the read after another transform in a SchemaTransform composition that supplied inputs.
Common situations: Misusing the SchemaTransform API by chaining a read transform after a write/transform; building a pipeline graph where the read node is accidentally given an input edge; YAML pipeline where the read is used as a non-root step.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Failed to convert Firestore document to Beam Row
- Firestore project id must be set on the transform or…
- translated with URN ' ' but payload was not a
- 2xx codes should not be exceptions. Got status code
- A 'datagen' table requires either 'rows-per-second' (for…
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/7aae6d140680c8f1.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/firestore/FirestoreReadSchemaTransformProvider.java:96
}
@Override
public List<String> outputCollectionNames() {
return Collections.singletonList(OUTPUT_TAG_NAME);
}
private static class FirestoreReadSchemaTransform extends SchemaTransform {
private final FirestoreReadSchemaTransformConfiguration configuration;
FirestoreReadSchemaTransform(FirestoreReadSchemaTransformConfiguration configuration) {
configuration.validate();
this.configuration = configuration;
}
@Override
public PCollectionRowTuple expand(PCollectionRowTuple input) {
if (!input.getAll().isEmpty()) {
throw new IllegalStateException(
"Firestore read transform does not expect input PCollections.");
}
Schema schema = JsonUtils.beamSchemaFromJsonSchema(configuration.getSchema());
String projectId = resolveProjectId(input.getPipeline());
String databaseId = resolveDatabaseId(input.getPipeline());
String parent = FirestoreUtils.documentsRoot(projectId, databaseId);
PCollection<ListDocumentsRequest> requests =
input
.getPipeline()
.apply("CreateCollectionId", Create.of(configuration.getCollectionId()))
.apply(
"BuildListDocumentsRequest",
ParDo.of(
new DoFn<String, ListDocumentsRequest>() {
@ProcessElement
public void processElement(View on GitHub (pinned to 12126d8942)