apache/beam · error · IllegalArgumentException

DLQ Router only supports PCollectionTuples split between…

Error message

DLQ Router only supports PCollectionTuples split between two message groupings

What it means

DLQRouter.expand() requires a PCollectionTuple containing exactly the two configured TupleTags — the 'good' messages tag and the 'bad' (dead-letter) messages tag. If any additional TupleTag/PCollection is present in the tuple (after removing the two known tags), it throws IllegalArgumentException because the router has no destination for extra outputs.

Solutions

  1. Build a dedicated PCollectionTuple containing only the goodMessages and badMessages tags before expanding the router.
  2. Route any extra output PCollections to other sinks separately instead of passing them through the DLQRouter.
  3. Verify the TupleTag instances passed to DLQRouter.of(...) are exactly the ones used to build the tuple (same tag objects/ids).

Example fix

// before
PCollectionTuple tuple = PCollectionTuple.of(good, goodPc).and(bad, badPc).and(metrics, metricsPc);
dLQRouter.expand(tuple); // throws
// after
PCollectionTuple tuple = PCollectionTuple.of(good, goodPc).and(bad, badPc);
dLQRouter.expand(tuple);
Defensive patterns

Strategy: validation

Validate before calling

Map<TupleTag<?>, PCollection<?>> tags = tuple.getAll();
if (tags.size() != 2 || !tags.containsKey(goodTag) || !tags.containsKey(badTag)) {
  throw new IllegalArgumentException("DLQRouter tuple must contain exactly good and bad tags");
}

Try / catch

try {
  dlqRouter.expand(tuple);
} catch (IllegalArgumentException e) {
  // rebuild a two-tag PCollectionTuple and retry the expansion
}

Prevention

When it happens

Trigger: Applying DLQRouter.expand() to a PCollectionTuple that was built with more than two tags, e.g. one that also carries metrics, retry, or side-output PCollections from a multi-output transform.

Common situations: Wiring the DLQ router downstream of a ParDo producing multiple output tags, or reusing a shared PCollectionTuple that accumulated extra keys instead of constructing a fresh two-tag tuple for the router.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at sdks/java/io/components/src/main/java/org/apache/beam/sdk/io/components/deadletterqueue/DLQRouter.java:53

  private final PTransform<@NonNull PCollection<K>, ?> errorSink;

  public DLQRouter(
      TupleTag<T> goodMessages,
      TupleTag<K> badMessages,
      PTransform<@NonNull PCollection<K>, ?> errorSink) {
    this.goodMessages = goodMessages;
    this.badMessages = badMessages;
    this.errorSink = errorSink;
  }

  @Override
  public PCollection<T> expand(@NonNull PCollectionTuple input) {
    // validate no extra messages are dropped
    Map<TupleTag<?>, PCollection<?>> pcollections = new HashMap<>(input.getAll());
    pcollections.remove(goodMessages);
    pcollections.remove(badMessages);
    if (pcollections.size() != 0) {
      throw new IllegalArgumentException(
          "DLQ Router only supports PCollectionTuples split between two message groupings");
    }

    input.get(badMessages).apply(errorSink);

    return input.get(goodMessages);
  }
}

View on GitHub (pinned to 12126d8942)