apache/beam · error

mismatched number of unnamed outputs: received

Error message

mismatched number of unnamed outputs:
received - %v
expected - 1

What it means

When a cross-language transform is declared with the unnamed output tag, the expanded transform is expected to produce exactly one output. This panic fires when the unnamed tag is used but the expansion returned multiple outputs.

Solutions

  1. Declare each expanded output explicitly with named tags instead of a single unnamed output.
  2. Use an expansion-service transform version that matches the expected single-output shape.
  3. Inspect the expansion response and pick the correct output tag before wiring consumers.

Example fix

// before
x := xlang.CrossTransform(scope, ext) // implicit single unnamed output
// after
x := xlang.CrossTransform(scope, ext, xlang.Output("main", "a"), xlang.Output("side", "b"))
Defensive patterns

Strategy: validation

Validate before calling

if usesUnnamedOutput && len(expandedOutputs) > 1 {
    return fmt.Errorf("multi-output transform requires named tags, got %d outputs", len(expandedOutputs))
}

Try / catch

defer func() {
    if r := recover(); r != nil {
        log.Fatalf("unnamed output used with multi-output transform: %v", r)
    }
}()

Prevention

When it happens

Trigger: Expanding an external transform with a single unnamed output declared while the expansion service response contains more than one output PCollection.

Common situations: Using the default/unnamed output mapping with a multi-output transform; SDK version where a previously single-output transform gained extra outputs.

Related errors


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

Appendix: source

Thrown at sdks/go/pkg/beam/core/runtime/graphx/xlang.go:144

// VerifyNamedOutputs ensures the expanded outputs correspond to the correct and expected named outputs
func VerifyNamedOutputs(ext *graph.ExternalTransform) {
	transform, err := ExpandedTransform(ext.Expanded)
	if err != nil {
		panic(err)
	}
	expandedOutputs := transform.GetOutputs()

	if len(expandedOutputs) != len(ext.OutputsMap) {
		panic(errors.Errorf("mismatched number of named outputs:\nreceived - %v\nexpected - %v", len(expandedOutputs), len(ext.OutputsMap)))
	}

	for tag := range ext.OutputsMap {
		_, exists := expandedOutputs[tag]
		if tag != graph.UnnamedOutputTag && !exists {
			panic(errors.Errorf("missing named output in expanded transform: %v is expected in %v", tag, expandedOutputs))
		}
		if tag == graph.UnnamedOutputTag && len(expandedOutputs) > 1 {
			panic(errors.Errorf("mismatched number of unnamed outputs:\nreceived - %v\nexpected - 1", len(expandedOutputs)))
		}
	}
}

// ResolveOutputIsBounded updates each Output node with respect to the received
// expanded components to reflect if it is bounded or not
func ResolveOutputIsBounded(e *graph.MultiEdge, isBoundedUpdater func(*graph.Node, bool)) {
	ext := e.External
	exp := ext.Expanded
	components, err := ExpandedComponents(exp)
	if err != nil {
		panic(err)
	}
	expandedPCollections := components.GetPcollections()
	transform, err := ExpandedTransform(exp)
	if err != nil {
		panic(err)
	}

View on GitHub (pinned to 12126d8942)