apache/beam · error · IllegalArgumentException
Expected outputs, but got exactly one.
Error message
Expected ${outputTags.size()} outputs, but got exactly one. What it means
When outputTags are declared and the external transform returns a single PCollection, YamlTransform wraps it under one tag. If the declared outputTags count is not exactly 1, the single PCollection cannot satisfy the declared multi-output contract and IllegalArgumentException is thrown.
Solutions
- Declare exactly one output tag, or none, if the transform yields a single PCollection.
- Fix the external transform to actually emit all declared outputs (use withMultipleOutputs).
- Remove the extra declared output tags from the YAML spec.
- Check the transform docs for the true number of outputs.
Example fix
// before
.withOutputTags(ImmutableList.of(new TupleTag<>("a"), new TupleTag<>("b")), ...)
// after (transform emits one output)
.withOutputTags(ImmutableList.of(new TupleTag<>("a")), ...); Defensive patterns
Strategy: validation
Validate before calling
if (outputTags.size() > 1 && transformEmitsSinglePCollection) {
throw new IllegalStateException("Cannot declare " + outputTags.size() + " outputs for single-output transform");
} Type guard
boolean outputCountMatches(int declared, Object out) { return declared == 1 || !(out instanceof PCollection); } Try / catch
try {
return expand(input);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("outputs, but got exactly one")) { /* fix output tag count */ }
throw e;
} Prevention
- Declare outputTags count matching the transform's real output arity
- Review external transform docs before multi-output specs
- Add smoke tests for each YAML transform spec
When it happens
Trigger: Declaring 2+ output tags for a YamlTransform whose underlying external transform returns just one PCollection (single-output external transform paired with a multi-output spec).
Common situations: YAML spec over-declares outputs for a transform that was updated to emit only one; confusing single- and multi-output variants of the same Python transform.
Related errors
- Expected a single PCollection output, but got
- Input has tags but expected input tags
- Output has tags but expected output tags
- Unrecognized input type
- A function must be provided to convert the input type into…
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/c89520da743f837e.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/java/extensions/yaml/src/main/java/org/apache/beam/sdk/extensions/yaml/YamlTransform.java:185
} else if (input instanceof PCollectionRowTuple) {
output = ((PCollectionRowTuple) input).apply(externalTransform);
} else {
throw new IllegalArgumentException("Unrecognized input type: " + input);
}
if (outputTags == null) {
if (!(output instanceof PCollection)) {
throw new IllegalArgumentException(
"Expected a single PCollection output, but got "
+ output
+ ". Perhaps withMultipleOutputs() needs to be specified?");
}
return (OutputT) output;
} else {
if (output instanceof PCollection) {
// ExternalPythonTransform always returns single outputs as PCollections.
if (outputTags.size() != 1) {
throw new IllegalArgumentException(
"Expected " + outputTags.size() + " outputs, but got exactly one.");
}
return (OutputT)
PCollectionRowTuple.of(outputTags.iterator().next(), (PCollection<Row>) output);
} else {
Map<TupleTag<?>, PValue> expandedOutputs = output.expand();
Set<String> actualOutputTags =
expandedOutputs.keySet().stream()
.map(TupleTag::getId)
.collect(Collectors.toCollection(HashSet::new));
if (!outputTags.equals(actualOutputTags)) {
throw new IllegalArgumentException(
"Output has tags "
+ Joiner.on(", ").join(actualOutputTags)
+ " but expected output tags "
+ Joiner.on(", ").join(outputTags));
}
PCollectionRowTuple result = PCollectionRowTuple.empty(input.getPipeline());View on GitHub (pinned to 12126d8942)