{"record":{"id":"aba6836e4a897775","repo":"apache/beam","slug":"dlq-router-only-supports-pcollectiontuples-split-between-two","errorCode":null,"errorMessage":"DLQ Router only supports PCollectionTuples split between two message groupings","messagePattern":"DLQ Router only supports PCollectionTuples split between two message groupings","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"sdks/java/io/components/src/main/java/org/apache/beam/sdk/io/components/deadletterqueue/DLQRouter.java","lineNumber":53,"sourceCode":"  private final PTransform<@NonNull PCollection<K>, ?> errorSink;\n\n  public DLQRouter(\n      TupleTag<T> goodMessages,\n      TupleTag<K> badMessages,\n      PTransform<@NonNull PCollection<K>, ?> errorSink) {\n    this.goodMessages = goodMessages;\n    this.badMessages = badMessages;\n    this.errorSink = errorSink;\n  }\n\n  @Override\n  public PCollection<T> expand(@NonNull PCollectionTuple input) {\n    // validate no extra messages are dropped\n    Map<TupleTag<?>, PCollection<?>> pcollections = new HashMap<>(input.getAll());\n    pcollections.remove(goodMessages);\n    pcollections.remove(badMessages);\n    if (pcollections.size() != 0) {\n      throw new IllegalArgumentException(\n          \"DLQ Router only supports PCollectionTuples split between two message groupings\");\n    }\n\n    input.get(badMessages).apply(errorSink);\n\n    return input.get(goodMessages);\n  }\n}\n","sourceCodeStart":35,"sourceCodeEnd":62,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/io/components/src/main/java/org/apache/beam/sdk/io/components/deadletterqueue/DLQRouter.java#L35-L62","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Build a dedicated PCollectionTuple containing only the goodMessages and badMessages tags before expanding the router.","Route any extra output PCollections to other sinks separately instead of passing them through the DLQRouter.","Verify the TupleTag instances passed to DLQRouter.of(...) are exactly the ones used to build the tuple (same tag objects/ids)."],"exampleFix":"// before\nPCollectionTuple tuple = PCollectionTuple.of(good, goodPc).and(bad, badPc).and(metrics, metricsPc);\ndLQRouter.expand(tuple); // throws\n// after\nPCollectionTuple tuple = PCollectionTuple.of(good, goodPc).and(bad, badPc);\ndLQRouter.expand(tuple);","handlingStrategy":"validation","validationCode":"Map<TupleTag<?>, PCollection<?>> tags = tuple.getAll();\nif (tags.size() != 2 || !tags.containsKey(goodTag) || !tags.containsKey(badTag)) {\n  throw new IllegalArgumentException(\"DLQRouter tuple must contain exactly good and bad tags\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  dlqRouter.expand(tuple);\n} catch (IllegalArgumentException e) {\n  // rebuild a two-tag PCollectionTuple and retry the expansion\n}","preventionTips":["Build a fresh PCollectionTuple with only the good/bad tags for the router.","Send extra outputs (metrics, retries) through their own sinks.","Reuse the exact same TupleTag instances the router was constructed with."],"tags":["beam","pipeline","dead-letter-queue","validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}