apache/beam · error

bad combinefn

Error message

bad combinefn

What it means

EncodeMultiEdge wraps encodeFn failures for a CombineFn as 'bad combinefn' (also context-tagged 'encoding userfn'). Like DoFns, CombineFns must be serializable named types with resolvable references. The error indicates the graph encoder could not turn the CombineFn into its protobuf representation.

Solutions

  1. Check the wrapped inner error from encodeFn for the root cause
  2. Define the CombineFn as a named exported top-level type
  3. Register accumulator/result types used by the CombineFn
  4. Re-run pipeline construction after fixing the fn definition

Example fix

// before
beam.Combine(s, func(a, b int) int { return a + b }, in) // closure
// after
type sumFn struct{}
func (sumFn) CreateAccumulator() int { return 0 }
func (sumFn) AddInput(a, b int) int { return a + b }
func (sumFn) MergeAccumulators(a, b int) int { return a + b }
beam.Combine(s, sumFn{}, in)
Defensive patterns

Strategy: validation

Validate before calling

if !isSerializableCombineFn(edge.CombineFn) {
  return errors.New("CombineFn must be a named exported type with registered accumulator types")
}

Type guard

func isSerializableCombineFn(fn interface{}) bool {
  t := reflect.TypeOf(fn)
  return t != nil && t.Name() != "" && t.PkgPath() != ""
}

Try / catch

ref, err := encodeFn((*graph.Fn)(edge.CombineFn))
if err != nil {
  return fmt.Errorf("CombineFn %T not serializable: %w", edge.CombineFn, err)
}

Prevention

When it happens

Trigger: EncodeMultiEdge on an edge with edge.CombineFn != nil where encodeFn fails — closures, anonymous structs, unregistered custom accumulator types, or encode-time type resolution failures.

Common situations: beam.Combine with an inline/anonymous combine function; custom accumulator types lacking registration when shipping to a remote runner; refactoring that breaks recorded function paths.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


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

Appendix: source

Thrown at sdks/go/pkg/beam/core/runtime/graphx/serialize.go:57

// EncodeMultiEdge converts the preprocessed representation into the wire
// representation of the multiedge, capturing input and output type information.
func EncodeMultiEdge(edge *graph.MultiEdge) (*v1pb.MultiEdge, error) {
	ret := &v1pb.MultiEdge{}
	ret.Opcode = string(edge.Op)

	if edge.DoFn != nil {
		ref, err := encodeFn((*graph.Fn)(edge.DoFn))
		if err != nil {
			wrapped := errors.Wrap(err, "bad userfn")
			return nil, errors.WithContextf(wrapped, "encoding userfn %v", edge)
		}
		ret.Fn = ref
	}
	if edge.CombineFn != nil {
		ref, err := encodeFn((*graph.Fn)(edge.CombineFn))
		if err != nil {
			wrapped := errors.Wrap(err, "bad combinefn")
			return nil, errors.WithContextf(wrapped, "encoding userfn %v", edge)
		}
		ret.Fn = ref
	}
	if edge.WindowFn != nil {
		ret.WindowFn = encodeWindowFn(edge.WindowFn)
	}

	for _, in := range edge.Input {
		kind, err := encodeInputKind(in.Kind)
		if err != nil {
			wrapped := errors.Wrap(err, "bad input type")
			return nil, errors.WithContextf(wrapped, "encoding userfn %v", edge)
		}
		t, err := encodeFullType(in.Type)
		if err != nil {
			wrapped := errors.Wrap(err, "bad input type")
			return nil, errors.WithContextf(wrapped, "encoding userfn %v", edge)

View on GitHub (pinned to 12126d8942)