apache/beam · error

bind conflict for : !=

Error message

bind conflict for %v: %v != %v

What it means

While walking a type tree during typex.Bind, the same type-variable name maps to two different concrete reflect.Types. The walk detects the conflict and aborts, since one variable name cannot bind to inconsistent concrete types.

Solutions

  1. Unify the concrete types so the repeated variable binds to exactly one reflect.Type, or use distinct variables (T, U) in the model.
  2. Inspect the message: name is the variable, then the two conflicting concrete types; change whichever one is wrong in your Fn signature.
  3. If the inputs are genuinely different types, change your PTransform signature to use separate type parameters instead of reusing one.
  4. Call typex.Bind with the pair whose conflict the message flags to reproduce and confirm before editing larger code.

Example fix

// before — one variable bound to two types
func (fn *dedupFn) ProcessElement(a T, b T)
// pipeline gives a=string, b=int

// after — distinct variables
func (fn *dedupFn) ProcessElement(a T, b U)
Defensive patterns

Strategy: validation

Validate before calling

func bindsConsistently(models, types []typex.FullType) bool {
    m, err := typex.Bind(models, types)
    if err != nil {
        return false
    }
    for _, v := range m { _ = v }
    return true
}

Type guard

// Ensure each variable name appears with one concrete type before Bind:
seen := map[string]reflect.Type{}
for name, rt := range candidateBindings {
    if prev, ok := seen[name]; ok && prev != rt {
        return false // would cause bind conflict
    }
    seen[name] = rt
}

Try / catch

m, err := typex.Bind(models, types)
var conflict *errors.E
if err != nil && strings.Contains(err.Error(), "bind conflict") {
    return fmt.Errorf("variable bound to two types; split into distinct type variables: %w", err)
}

Prevention

When it happens

Trigger: A single type variable (e.g. T) appears in multiple positions of the model, and Bind is given concrete types that resolve T to different reflect.Types — e.g. X<T> paired with []string and X<int> paired with []int in the same Bind call.

Common situations: Users write a generic DoFn with a repeated type variable but feed heterogeneous input (e.g. KV<A,B> where they reused T for both A and B); also occurs when a custom aggregation or coders inference passes mismatched inputs to a multi-input step.

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/16c57c940fa3bfd0. Report an issue: GitHub.

Appendix: source

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

		}
		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() {
			return errors.Errorf("bind conflict for %v: %v != %v", name, current, model.Type())
		}
		m[name] = model.Type()
		return nil
	case Composite, Container:
		for i, elm := range t.Components() {
			if err := walk(elm, model.Components()[i], m); err != nil {
				return err
			}
		}
		return nil
	default:
		return nil
	}
}

// Substitute returns types identical to the given types, but with all
// universals substituted. All free type variables must be present in the
// substitution.

View on GitHub (pinned to 12126d8942)