apache/beam · error
typex.Bind: %v is not assignable to %v
Error message
typex.Bind: %v is not assignable to %v
What it means
typex.Bind creates a binding from type variables (models) to concrete Go types. Each actual type must be structurally assignable to its corresponding model; if IsStructurallyAssignable fails, Bind refuses to build the map and returns this error naming the model and the offending type.
Source
Thrown at sdks/go/pkg/beam/core/typex/fulltype.go:360
return true
}
// Bind returns a substitution from universals to types in the given models,
// such as {"T" -> X, "X" -> int}. Each model must be assignable to the
// corresponding type. For example, Bind(KV<T,int>, KV<string, int>) would
// produce {"T" -> string}.
func Bind(types, models []FullType) (map[string]reflect.Type, error) {
if len(types) != len(models) {
return nil, errors.Errorf("typex.Bind: invalid number of models: %v, want %v", len(models), len(types))
}
m := make(map[string]reflect.Type)
for i := 0; i < len(types); i++ {
t := types[i]
model := models[i]
if !IsStructurallyAssignable(model, t) {
return nil, errors.Errorf("typex.Bind: %v is not assignable to %v", model, t)
}
if err := walk(t, model, m); err != nil {
return nil, err
}
}
return m, nil
}
func walk(t, model FullType, m map[string]reflect.Type) error {
switch t.Class() {
case Universal:
// By checking that the model is assignable to t, we know that they are
// structurally compatible. We rely on the exact reflect.Type in the
// Aggregate case to pick the correct binding, i.e., we do not need to
// construct such a type.
name := t.Type().Name()
if current, ok := m[name]; ok && current != model.Type() {View on GitHub (pinned to 12126d8942)
Solutions
- Check the two %v values in the message: the first is the model (type variable) and the second your concrete type; align the concrete type's structure with the model.
- Verify argument order — Bind expects (models, types); swapping them produces spurious assignment failures.
- Use typex.New / typex.T, typex.X etc. to construct models matching the exact container/composite shape of your runtime type.
- Print reflect.TypeOf on the value and compare element kinds (slice vs array, key/value types) with the model before calling Bind.
Example fix
// before
typex.Bind([]typex.FullType{typex.New(typex.X(typex.T))}, []typex.FullType{typex.New(reflect.TypeOf(42))}) // X<int> model vs int
// after
typex.Bind([]typex.FullType{typex.New(typex.T)}, []typex.FullType{typex.New(reflect.TypeOf(42))}) Defensive patterns
Strategy: validation
Validate before calling
func canBind(models, types []typex.FullType) bool {
if len(models) != len(types) {
return false
}
for i := range models {
if !typex.IsStructurallyAssignable(models[i], types[i]) {
return false
}
}
return true
} Type guard
if mt, ok := value.(typex.FullType); ok && typex.IsStructurallyAssignable(model, mt) { /* safe to Bind */ } Try / catch
m, err := typex.Bind(models, types)
if err != nil {
return fmt.Errorf("type binding failed: %w", err)
} Prevention
- Construct models with typex.T/typex.X helpers that mirror the exact container shape of your runtime types.
- Always pass concrete reflect-derived types (typex.New(reflect.TypeOf(v))) rather than guessed FullTypes.
- Check len(models) == len(types) before calling Bind.
- Add a unit test that Binds your Fn's signature against the actual PCollection element types.
When it happens
Trigger: Calling typex.Bind(models, types) where the i-th concrete type does not structurally match the i-th model — e.g. binding an int against a variable typed as X<int>, mismatched container element types, or supplying fewer/mismatched pairs so models[i] cannot accept types[i].
Common situations: Users defining a DoFn or CombineFn with generic type variables pass user data whose shape doesn't match the declared variable structure; common after refactoring a pipeline's PCollection element type (e.g. T to string) without updating the typex.NewVariable/T types passed to Bind.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- GroupIntoBatchesWithShardedKey: input PCollection must be KV
- GroupIntoBatchesWithShardedKey: type parameter K (%v) does n
- bind conflict for %v: %v != %v
- optional generic parameter not bound %v
- cannot substitute type %v with %v, already defined as %v
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/22778e38e5354752.
Report an issue: GitHub.