{"record":{"id":"1dcefdd9037ff7e2","repo":"apache/beam","slug":"cannot-substitute-type-v-with-v-already-defined-as-v","errorCode":null,"errorMessage":"cannot substitute type %v with %v, already defined as %v","messagePattern":"cannot substitute type (.+?) with (.+?), already defined as (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/core/graph/bind.go","lineNumber":84,"sourceCode":"\t\treturn errors.WithContextf(err, \"binding fn %v\", fn.Fn.Name())\n\t}\n\n\tinbound, kinds, err := findInbound(fn, in...)\n\tif err != nil {\n\t\treturn nil, nil, nil, nil, addContext(err, fn)\n\t}\n\toutbound, err := findOutbound(fn)\n\tif err != nil {\n\t\treturn nil, nil, nil, nil, addContext(err, fn)\n\t}\n\n\tsubst, err := typex.Bind(inbound, in)\n\tif err != nil {\n\t\treturn nil, nil, nil, nil, addContext(err, fn)\n\t}\n\tfor k, v := range typedefs {\n\t\tif substK, exists := subst[k]; exists {\n\t\t\terr := errors.Errorf(\"cannot substitute type %v with %v, already defined as %v\", k, v, substK)\n\t\t\treturn nil, nil, nil, nil, addContext(err, fn)\n\t\t}\n\t\tsubst[k] = v\n\t}\n\n\tout, err := typex.Substitute(outbound, subst)\n\tif err != nil {\n\t\treturn nil, nil, nil, nil, addContext(err, fn)\n\t}\n\treturn inbound, kinds, outbound, out, nil\n}\n\nfunc findOutbound(fn *funcx.Fn) ([]typex.FullType, error) {\n\tret := trimIllegal(returnTypes(funcx.SubReturns(fn.Ret, fn.Returns(funcx.RetValue)...)))\n\tparams := funcx.SubParams(fn.Param, fn.Params(funcx.FnEmit)...)\n\n\tvar outbound []typex.FullType\n","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/core/graph/bind.go#L66-L102","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nBind(fn, map[string]reflect.Type{\"X\": reflect.TypeOf(\"\")}) // fn already binds X to int\n// after\nBind(fn, nil) // let inference handle it","handlingStrategy":"validation","validationCode":"for k, v := range typedefs {\n    if inferred, ok := inferGenericBindings(fn)[k]; ok && inferred != v {\n        return fmt.Errorf(\"typedef %s=%v conflicts with inferred %v\", k, v, inferred)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["go","beam","generics","type-binding","dofn"],"backgroundTag":"conflicting-config-options","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}