apache/beam · error

missing named output in expanded transform

Error message

missing named output in expanded transform: %v is expected in %v

What it means

During VerifyNamedOutputs each expected tag from ext.OutputsMap must exist in the expanded transform's outputs. This panic fires when a named (non-empty) tag is declared locally but absent from the expansion service's expanded outputs.

Solutions

  1. Correct output tag names to exactly match the remote transform's output tags.
  2. Verify the expansion URN and underlying method select the intended transform version.
  3. Query the expansion service for the transform's output tag list and regenerate the outputs map.

Example fix

// before
xlang.Output("outpu", "a") // typo
// after
xlang.Output("output", "a")
Defensive patterns

Strategy: validation

Validate before calling

for tag := range outputsMap {
    if tag != "" {
        if _, ok := expandedOutputs[tag]; !ok {
            return fmt.Errorf("tag %q not in expanded outputs %v", tag, expandedOutputs)
        }
    }
}

Try / catch

defer func() {
    if r := recover(); r != nil {
        log.Fatalf("missing named output during expansion: %v", r)
    }
}()

Prevention

When it happens

Trigger: An output tag passed to beam.CrossTransform/Output is not present in the expandedOutputs map returned by the expansion service, while total counts happen to match.

Common situations: Typo or naming mismatch between local output tag and the remote transform's output tag; using the wrong URN/method so a different transform variant is expanded; version drift between SDK and expansion service.

Related errors


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

Appendix: source

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

	}
}

// 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)

View on GitHub (pinned to 12126d8942)