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
- Remove the conflicting typedef and let Bind infer the type from the function signature.
- Rename the generic variable in the typedefs so it does not collide with the inferred binding.
- 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
- Pass typedefs only for generics the framework cannot infer.
- Never reuse a generic variable name already bound by the function's actual types.
- Drop stale typedef maps when changing a DoFn's signature.
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
- bind conflict for %v: %v != %v
- optional generic parameter not bound %v
- first main input parameter must be a value type
- ProcessElement uses a TimerProvider, but no Timer fields are
- bad parameter type for %s: %v
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/1dcefdd9037ff7e2.
Report an issue: GitHub.