{"record":{"id":"1f9b6b9b1b8c3dfd","repo":"apache/beam","slug":"expected-single-output-pcollection-in-reshuffle","errorCode":null,"errorMessage":"Expected single output PCollection in reshuffle: ","messagePattern":"Expected single output PCollection in reshuffle: ","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/runners/prism/internal/handlerunner.go","lineNumber":188,"sourceCode":"\t}\n\n\t// A Reshuffle, in principle, is a no-op on the pipeline structure, WRT correctness.\n\t// It could however affect performance, so it exists to tell the runner that this\n\t// point in the pipeline needs a fusion break, to enable the pipeline to change it's\n\t// degree of parallelism.\n\t//\n\t// The change of parallelism goes both ways. It could allow for larger batch sizes\n\t// enable smaller batch sizes downstream if it is infact paralleizable.\n\t//\n\t// But for a single transform node per stage runner, we can elide it entirely,\n\t// since the input collection and output collection types match.\n\n\t// Get the input and output PCollections, there should only be 1 each.\n\tif len(t.GetInputs()) != 1 {\n\t\tpanic(\"Expected single input PCollection in reshuffle: \" + prototext.Format(t))\n\t}\n\tif len(t.GetOutputs()) != 1 {\n\t\tpanic(\"Expected single output PCollection in reshuffle: \" + prototext.Format(t))\n\t}\n\n\tinColID := getOnlyValue(t.GetInputs())\n\toutColID := getOnlyValue(t.GetOutputs())\n\n\t// We need to find all Transforms that consume the output collection and\n\t// replace them so they consume the input PCollection directly.\n\n\t// We need to remove the consumers of the output PCollection.\n\ttoRemove := []string{}\n\t// We need to force the consumers to be stage root,\n\t// because reshuffle should be a fusion break.\n\tforcedRoots := []string{}\n\n\tfor tid, t := range comps.GetTransforms() {\n\t\tfor li, gi := range t.GetInputs() {\n\t\t\tif gi == outColID {\n\t\t\t\tt.GetInputs()[li] = inColID","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/runners/prism/internal/handlerunner.go#L170-L206","documentation":"Symmetric to the input check: a Reshuffle transform must produce exactly one output PCollection so prism can elide it and reconnect consumers to the input collection. A multi-output reshuffle means the graph violates the runner's assumption and it panics with the transform dump.","triggerScenarios":"handleReshuffle receives a PTransform whose outputs map has zero or multiple entries — e.g. a malformed or custom-built Reshuffle node, or post-processing that added outputs to the transform.","commonSituations":"Hand-built or tool-generated pipeline graphs; graph rewriters that fan reshuffle output to multiple collections before submission; corrupted or doctored job-submission requests.","solutions":["Read the prototext dump to see the outputs attached to the reshuffle transform.","Rebuild the pipeline with standard beam.Reshuffle() so the SDK emits exactly one output.","Fix custom graph-rewriting code so reshuffle nodes keep exactly one output PCollection.","Report upstream if a normal pipeline yields multi-output reshuffles."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if isReshuffle(t) && len(t.GetOutputs()) != 1 { /* fix graph before submission */ }","typeGuard":null,"tryCatchPattern":"defer func(){ if r := recover(); r != nil && strings.Contains(fmt.Sprint(r), \"Expected single output PCollection in reshuffle\") { /* inspect transform dump */ } }()","preventionTips":["Never fan reshuffle output to multiple collections","Regenerate graphs with the standard SDK APIs"],"tags":["go","apache-beam","prism-runner","reshuffle"],"backgroundTag":"internal-invariant-violation","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"}