apache/beam · error · java.lang.IllegalArgumentException

Found conflicting TupleTags in flattened expansion of

Error message

Found conflicting TupleTags in flattened expansion of <partiallyExpanded>: <key> maps to <previous> and <value>

What it means

PValues.fullyExpand() flattens a PValue's expansion tree into a Map<TupleTag, PCollection>. If two entries share the same TupleTag mapping to different PCollections, the flattened map would silently lose data, so Beam throws IllegalArgumentException. This indicates a composite transform returning duplicate TupleTags for distinct outputs.

Solutions

  1. Give each output a distinct TupleTag (unique id: new TupleTag<String>("uniqueName"){}`).
  2. Check the composite transform's expand() to ensure each TupleTag maps to exactly one PCollection.
  3. If outputs genuinely coincide, return a single PCollection instead of a multi-output mapping.
  4. Rename duplicated TupleTag constants created by copy-paste.

Example fix

// before
TupleTag<String> out = new TupleTag<String>("out") {};
TupleTag<Integer> other = new TupleTag<Integer>("out") {}; // same tag id -> conflict
// after
TupleTag<String> out = new TupleTag<String>("out") {};
TupleTag<Integer> other = new TupleTag<Integer>("outInt") {};
Defensive patterns

Strategy: validation

Validate before calling

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

Try / catch

try {
  PCollectionTuple pct = composite.expand();
  PValues.expandOutput(...);
} catch (IllegalArgumentException e) {
  throw new IllegalStateException("Composite transform reuses a TupleTag: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: A multi-output composite PTransform (PTransform with PCollectionTuple/TupleTag outputs) reuses the same TupleTag for two different output PCollections, and the result is expanded via expandOutput/expandInput/expandValue.

Common situations: Defining TupleTag fields with identical default tags (e.g. new TupleTag<String>() {} declared twice with the same anonymous type); copying output maps manually; refactor merging two outputs into one tag.

Related errors


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

Appendix: source

Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/values/PValues.java:64

   *   <li>{@link POutput#expand} (users can write custom POutputs)
   *   <li>{@link PInput#expand} (users can write custom PInputs)
   *   <li>{@link PTransform#getAdditionalInputs} (users can have their composites report inputs not
   *       passed by {@link PCollection#apply})
   * </ul>
   *
   * <p>These all return {@code Map<TupleTag<?> PValue>}. A user's implementation of these methods
   * is permitted to return either a {@link PCollection} or a {@link PCollectionView} for each
   * PValue. PCollection's expand to themselves and {@link PCollectionView} expands to the {@link
   * PCollection} that it is a view of.
   */
  public static Map<TupleTag<?>, PCollection<?>> fullyExpand(
      Map<TupleTag<?>, PValue> partiallyExpanded) {
    Map<TupleTag<?>, PCollection<?>> result = new LinkedHashMap<>();
    for (Map.Entry<TupleTag<?>, PValue> pvalue : partiallyExpanded.entrySet()) {
      if (pvalue.getValue() instanceof PCollection) {
        PCollection<?> previous = result.put(pvalue.getKey(), (PCollection<?>) pvalue.getValue());
        if (previous != null) {
          throw new IllegalArgumentException(
              String.format(
                  "Found conflicting %ss in flattened expansion of %s: %s maps to %s and %s",
                  partiallyExpanded,
                  TupleTag.class.getSimpleName(),
                  pvalue.getKey(),
                  previous,
                  pvalue.getValue()));
        }
      } else {
        if (pvalue.getValue().expand().size() == 1
            && Iterables.getOnlyElement(pvalue.getValue().expand().values())
                .equals(pvalue.getValue())) {
          throw new IllegalStateException(
              String.format(
                  "Non %s %s that expands into itself %s",
                  PCollection.class.getSimpleName(),
                  PValue.class.getSimpleName(),
                  pvalue.getValue()));

View on GitHub (pinned to 12126d8942)