apache/beam · error

cannot substitute type %v with %v, already defined as %v

Error message

cannot substitute type %v with %v, already defined as %v

What it means

Bind in sdks/go/pkg/beam/core/graph/bind.go computes the generic type substitution for a DoFn or combine function, then merges caller-supplied typedefs into that map. This error occurs when a typedef key collides with a generic variable that typex.Bind already bound to a concrete type, so the explicit typedef would conflict with the inferred binding.

Source

Thrown at sdks/go/pkg/beam/core/graph/bind.go:84

		return errors.WithContextf(err, "binding fn %v", fn.Fn.Name())
	}

	inbound, kinds, err := findInbound(fn, in...)
	if err != nil {
		return nil, nil, nil, nil, addContext(err, fn)
	}
	outbound, err := findOutbound(fn)
	if err != nil {
		return nil, nil, nil, nil, addContext(err, fn)
	}

	subst, err := typex.Bind(inbound, in)
	if err != nil {
		return nil, nil, nil, nil, addContext(err, fn)
	}
	for k, v := range typedefs {
		if substK, exists := subst[k]; exists {
			err := errors.Errorf("cannot substitute type %v with %v, already defined as %v", k, v, substK)
			return nil, nil, nil, nil, addContext(err, fn)
		}
		subst[k] = v
	}

	out, err := typex.Substitute(outbound, subst)
	if err != nil {
		return nil, nil, nil, nil, addContext(err, fn)
	}
	return inbound, kinds, outbound, out, nil
}

func findOutbound(fn *funcx.Fn) ([]typex.FullType, error) {
	ret := trimIllegal(returnTypes(funcx.SubReturns(fn.Ret, fn.Returns(funcx.RetValue)...)))
	params := funcx.SubParams(fn.Param, fn.Params(funcx.FnEmit)...)

	var outbound []typex.FullType

View on GitHub (pinned to 12126d8942)

Solutions

  1. Remove the conflicting typedef and let Bind infer the type from the function signature.
  2. Rename the generic variable in the typedefs so it does not collide with the inferred binding.
  3. Fix the function's actual parameter types if the inferred binding is wrong and the typedef expresses intent.

Example fix

// before
Bind(fn, map[string]reflect.Type{"X": reflect.TypeOf("")}) // fn already binds X to int
// after
Bind(fn, nil) // let inference handle it
Defensive patterns

Strategy: validation

Validate before calling

for k, v := range typedefs {
    if inferred, ok := inferGenericBindings(fn)[k]; ok && inferred != v {
        return fmt.Errorf("typedef %s=%v conflicts with inferred %v", k, v, inferred)
    }
}

Prevention

When it happens

Trigger: Calling graph.Bind (via newDoFnNode or NewCombine) with typedefs containing a key that equals a generic variable name already bound by the function's actual parameter types, e.g. passing typex.T for X while the DoFn's input already pins X to a concrete type.

Common situations: Registering a DoFn with beam.RegisterDoFn-style helpers while also supplying redundant/conflicting type hints; combine functions where NewCombine infers accumulator types and user typedefs disagree; stale typedef maps copied from another function registration.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


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