apache/beam · error · IllegalArgumentException
Unknown output tag
Error message
Unknown output tag %s
What it means
Thrown by the bundle-context OutputReceiver (used for FinishBundle/StartBundle emissions) when MultiOutputReceiver.output is called with a TupleTag whose id has no registered consumer in localNameToConsumer. The tag must correspond to an output declared in the PCollection/PCollectionTuple wired into the pipeline step.
Solutions
- Register the TupleTag in the PCollectionTuple/apply call that declares the transform's outputs (withOutputTag) before emitting to it.
- Reuse the exact same TupleTag instance (or same id) for both registration and emission — create them as static final constants.
- Log available output tag ids and compare with the tag being used to spot id mismatches.
- If the tag is intentionally absent (conditional outputs), guard the emission with a check that the output was declared.
Example fix
// before
TupleTag<String> extra = new TupleTag<String>() {}; // new instance, different id
c.output(extra, value);
// after
private static final TupleTag<String> EXTRA_TAG = new TupleTag<String>() {};
// register: tuple.withOutputTag(EXTRA_TAG, ...)
c.output(EXTRA_TAG, value); Defensive patterns
Strategy: validation
Validate before calling
Set<String> declaredTags = tuple.getAllTags().stream().map(TupleTag::getId).collect(Collectors.toSet());
if (!declaredTags.contains(tag.getId())) {
throw new IllegalArgumentException("Undeclared output tag: " + tag.getId());
} Try / catch
try {
ctx.output(tag, value);
} catch (IllegalArgumentException e) {
logger.error("Tag {} not registered; declared: {}", tag, declaredTags, e);
} Prevention
- Use static final TupleTag constants shared between registration and emission
- Register every emitted tag via withOutputTags/withOutputTag
- Add a pipeline-construction unit test that exercises all outputs
When it happens
Trigger: Calling context.output(tag, output, timestamp, window) (FinishBundleArgumentProvider.Context) with a TupleTag that is not among the transform's declared outputs; building the TupleTag with a different id string than the one registered for the output PCollection.
Common situations: Multi-output DoFns emitting to a tag created with `new TupleTag<>(){}` where a new anonymous instance differs from the registered one; typos in tag names; emitting from @FinishBundle to a tag not declared in PCollectionTuple; DoFn refactor adding outputs without updating the tuple registration.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- Attempting to call and() on a CoGbkResult apparently not…
- Can't convert 'null' to non-nullable field
- Cannot merge schemas with different numbers of fields…
- must have at least one input to a KeyedPCollections
- Provided class should be source or sink plugin
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/9f9f10930ca0a9d8.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/java/harness/src/main/java/org/apache/beam/fn/harness/FnApiDoFnRunner.java:1646
}
@Override
public PipelineOptions getPipelineOptions() {
return pipelineOptions;
}
@Override
public void output(OutputT output, Instant timestamp, BoundedWindow window) {
outputTo(
mainOutputConsumer, WindowedValues.of(output, timestamp, window, PaneInfo.NO_FIRING));
}
@Override
public <T> void output(TupleTag<T> tag, T output, Instant timestamp, BoundedWindow window) {
FnDataReceiver<WindowedValue<T>> consumer =
(FnDataReceiver) localNameToConsumer.get(tag.getId());
if (consumer == null) {
throw new IllegalArgumentException(String.format("Unknown output tag %s", tag));
}
outputTo(consumer, WindowedValues.of(output, timestamp, window, PaneInfo.NO_FIRING));
}
}
private final FinishBundleArgumentProvider.Context context =
new FinishBundleArgumentProvider.Context();
@Override
public DoFn<InputT, OutputT>.FinishBundleContext finishBundleContext(
DoFn<InputT, OutputT> doFn) {
return context;
}
@Override
public PipelineOptions pipelineOptions() {
return pipelineOptions;
}View on GitHub (pinned to 12126d8942)