apache/beam · error

Expected single output PCollection in reshuffle:

Error message

Expected single output PCollection in reshuffle: 

What it means

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.

Solutions

  1. Read the prototext dump to see the outputs attached to the reshuffle transform.
  2. Rebuild the pipeline with standard beam.Reshuffle() so the SDK emits exactly one output.
  3. Fix custom graph-rewriting code so reshuffle nodes keep exactly one output PCollection.
  4. Report upstream if a normal pipeline yields multi-output reshuffles.
Defensive patterns

Strategy: validation

Validate before calling

if isReshuffle(t) && len(t.GetOutputs()) != 1 { /* fix graph before submission */ }

Try / catch

defer func(){ if r := recover(); r != nil && strings.Contains(fmt.Sprint(r), "Expected single output PCollection in reshuffle") { /* inspect transform dump */ } }()

Prevention

When it happens

Trigger: 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.

Common situations: Hand-built or tool-generated pipeline graphs; graph rewriters that fan reshuffle output to multiple collections before submission; corrupted or doctored job-submission requests.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at sdks/go/pkg/beam/runners/prism/internal/handlerunner.go:188

	}

	// A Reshuffle, in principle, is a no-op on the pipeline structure, WRT correctness.
	// It could however affect performance, so it exists to tell the runner that this
	// point in the pipeline needs a fusion break, to enable the pipeline to change it's
	// degree of parallelism.
	//
	// The change of parallelism goes both ways. It could allow for larger batch sizes
	// enable smaller batch sizes downstream if it is infact paralleizable.
	//
	// But for a single transform node per stage runner, we can elide it entirely,
	// since the input collection and output collection types match.

	// Get the input and output PCollections, there should only be 1 each.
	if len(t.GetInputs()) != 1 {
		panic("Expected single input PCollection in reshuffle: " + prototext.Format(t))
	}
	if len(t.GetOutputs()) != 1 {
		panic("Expected single output PCollection in reshuffle: " + prototext.Format(t))
	}

	inColID := getOnlyValue(t.GetInputs())
	outColID := getOnlyValue(t.GetOutputs())

	// We need to find all Transforms that consume the output collection and
	// replace them so they consume the input PCollection directly.

	// We need to remove the consumers of the output PCollection.
	toRemove := []string{}
	// We need to force the consumers to be stage root,
	// because reshuffle should be a fusion break.
	forcedRoots := []string{}

	for tid, t := range comps.GetTransforms() {
		for li, gi := range t.GetInputs() {
			if gi == outColID {
				t.GetInputs()[li] = inColID

View on GitHub (pinned to 12126d8942)