apache/beam · error

typex.Bind: invalid number of models

Error message

typex.Bind: invalid number of models: %v, want %v

What it means

typex.Bind substitutes concrete types for universals in a FullType signature: each provided model must correspond one-to-one with the types being bound. This error is returned when len(models) != len(types), so the substitution cannot proceed — the caller supplied a mismatched number of model types. Exposed publicly, so users invoking Bind directly (or via TestBindSubstitute) hit it.

Solutions

  1. Ensure models has exactly one FullType per element of types, in order, before calling Bind.
  2. If binding composite types (KV, CoGBK), expand the signature into its component FullTypes and provide one model per component.
  3. Guard the call: check len(types) == len(models) or pass through the signature's full type list programmatically.
  4. Prefer deriving models from the DoFn signature (typex.FullSignature) rather than hard-coding lists so arity stays in sync.

Example fix

// before
subst, err := typex.Bind([]typex.FullType{tKV}, []typex.FullType{model}) // arity mismatch

// after
kv := tKV.(typex.KVFullType)
subst, err := typex.Bind(kv.Components(), []typex.FullType{kvModel, intModel})
Defensive patterns

Strategy: validation

Validate before calling

if len(models) != len(types) {
    return fmt.Errorf("typex.Bind needs %d models, got %d", len(types), len(models))
}

Type guard

func canBind(types, models []typex.FullType) bool {
    return len(types) == len(models)
}

Try / catch

subst, err := typex.Bind(types, models)
if err != nil {
    return nil, fmt.Errorf("bind failed (types=%d models=%d): %w", len(types), len(models), err)
}

Prevention

When it happens

Trigger: Calling typex.Bind with a different number of FullTypes than models — e.g. binding a single-type signature with two models, or passing a KV signature's components but forgetting one model; callers misusing Bind recursively (Bind calls itself on component types).

Common situations: Custom DoFn/framework code performing manual type substitution on multi-type signatures (KV, CoGBK); tests substituting universals with incomplete model lists; refactors that change arity of a signature without updating Bind call sites.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at sdks/go/pkg/beam/core/typex/fulltype.go:351

func IsBound(t FullType) bool {
	if t.Class() == Universal {
		return false
	}
	for _, elm := range t.Components() {
		if !IsBound(elm) {
			return false
		}
	}
	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 {

View on GitHub (pinned to 12126d8942)