apache/beam · error · IllegalArgumentException

TupleTag already present in this tuple

Error message

TupleTag already present in this tuple

What it means

PCollectionTuple.ofPrimitiveOutputsInternal builds its internal map of output tags to PCollections and throws IllegalArgumentException when the same TupleTag appears twice in the provided TupleTagList. Beam requires each output tag in a tuple to be unique so that tags can act as unambiguous keys for mapping outputs. This guard prevents silently overwriting or aliasing a tagged output.

Solutions

  1. Ensure each output in the PCollectionTuple uses a distinct TupleTag — create a new TupleTag for the second output instead of reusing the first.
  2. Review TupleTag equality: two TupleTag instances compare equal when constructed without an id only if same object; with explicit ids, duplicate id strings collide — use unique ids.
  3. If tags come from user config or a collection, validate uniqueness (e.g. by tag id) before building the PCollectionTuple.

Example fix

// before
TupleTag<String> out = new TupleTag<>("out");
PCollectionTuple.of(out, mainPc).and(out, sidePc); // throws
// after
TupleTag<String> outMain = new TupleTag<>("outMain");
TupleTag<String> outSide = new TupleTag<>("outSide");
PCollectionTuple.of(outMain, mainPc).and(outSide, sidePc);
Defensive patterns

Strategy: validation

Validate before calling

Set<TupleTag<?>> seen = new HashSet<>();
for (TupleTag<?> tag : tags) {
  if (!seen.add(tag)) throw new IllegalArgumentException("duplicate TupleTag: " + tag.getId());
}

Prevention

When it happens

Trigger: Calling PCollectionTuple.ofPCollections / PCollectionTuple.of (which routes into ofPrimitiveOutputsInternal) with a TupleTagList or set of (tag, PCollection) pairs in which the same TupleTag instance or an equal TupleTag (same id) is supplied more than once, e.g. PCollectionTuple.of(tag1, pc1).and(tag1, pc2).

Common situations: Reusing a single TupleTag field for multiple outputs in a ParDo with several side outputs; programmatically generating tags in a loop but reusing one tag variable; copy-pasted multi-output pipeline code where a tag was meant to be a new TupleTag<> but was cloned.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/0d10fb374e257933. Report an issue: GitHub.

Appendix: source

Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/values/PCollectionTuple.java:299

  /**
   * <b><i>For internal use only; no backwards-compatibility guarantees.</i></b>
   *
   * <p>Returns a {@link PCollectionTuple} with each of the given tags mapping to a new output
   * {@link PCollection}.
   *
   * <p>For use by primitive transformations only.
   */
  @Internal
  public static PCollectionTuple ofPrimitiveOutputsInternal(
      Pipeline pipeline,
      TupleTagList outputTags,
      Map<TupleTag<?>, Coder<?>> coders,
      WindowingStrategy<?, ?> windowingStrategy,
      IsBounded isBounded) {
    Map<TupleTag<?>, PCollection<?>> pcollectionMap = new LinkedHashMap<>();
    for (TupleTag<?> outputTag : outputTags.tupleTags) {
      if (pcollectionMap.containsKey(outputTag)) {
        throw new IllegalArgumentException("TupleTag already present in this tuple");
      }

      // In fact, `token` and `outputCollection` should have
      // types TypeDescriptor<T> and PCollection<T> for some
      // unknown T. It is safe to create `outputCollection`
      // with type PCollection<Object> because it has the same
      // erasure as the correct type. When a transform adds
      // elements to `outputCollection` they will be of type T.
      @SuppressWarnings("unchecked")
      PCollection<?> outputCollection =
          PCollection.createPrimitiveOutputInternal(
                  pipeline, windowingStrategy, isBounded, (Coder) coders.get(outputTag))
              .setTypeDescriptor(outputTag.getTypeDescriptor());

      pcollectionMap.put(outputTag, outputCollection);
    }
    return new PCollectionTuple(pipeline, pcollectionMap);
  }

View on GitHub (pinned to 12126d8942)