{"record":{"id":"b378bc926fa75e81","repo":"apache/beam","slug":"found-v-emitters-want-v","errorCode":null,"errorMessage":"found %v emitters, want %v","messagePattern":"found (.+?) emitters, want (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/core/runtime/exec/fn.go","lineNumber":496,"sourceCode":"\t\t\treturn nil, errors.WithContextf(err, \"making side input %v for %v\", i, fn)\n\t\t}\n\t\tret = append(ret, s)\n\t}\n\treturn ret, nil\n}\n\nfunc makeEmitters(fn *funcx.Fn, nodes []Node) ([]ReusableEmitter, error) {\n\tif len(nodes) == 0 {\n\t\treturn nil, nil // ok: no output nodes\n\t}\n\n\toffset := 0\n\tif len(fn.Returns(funcx.RetValue)) > 0 {\n\t\toffset = 1\n\t}\n\tout := fn.Params(funcx.FnEmit)\n\tif len(out) != len(nodes)-offset {\n\t\treturn nil, errors.Errorf(\"found %v emitters, want %v\", len(out), len(nodes)-offset)\n\t}\n\n\tvar ret []ReusableEmitter\n\tfor i := 0; i < len(out); i++ {\n\t\tparam := fn.Param[out[i]]\n\t\tret = append(ret, makeEmit(param.T, nodes[i+offset]))\n\t}\n\treturn ret, nil\n}\n\n// makeSideInput returns a reusable side input of the given kind and type.\nfunc makeSideInput(kind graph.InputKind, t reflect.Type, values ReStream) (ReusableInput, error) {\n\tswitch kind {\n\tcase graph.Singleton:\n\t\telms, err := ReadAll(values)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}","sourceCodeStart":478,"sourceCodeEnd":514,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/core/runtime/exec/fn.go#L478-L514","documentation":"makeEmitters matches the DoFn's emit-typed parameters (FnEmit) to the output nodes of the transform. The number of emit params must equal the number of output nodes minus an offset of 1 when the function also returns a value (the single return maps to the first node). If the counts disagree, the transform cannot route emitted elements to outputs.","triggerScenarios":"Up() -> makeEmitters where fn.Params(FnEmit) count != len(nodes) - (1 if fn.Returns(FnValue) > 0), e.g. a DoFn with 2 emit params wired to a node with 1 output, or a DoFn that both returns a value and has emit params but only one output node.","commonSituations":"Adding a second emit parameter (multi-output DoFn) without adding the corresponding beam.ParDo output (must pass two PCollections / beam.Pers outputs); declaring an output in the pipeline but removing the emit param from the DoFn; mixing return-value style with emit style and miscounting nodes.","solutions":["Match the number of outputs passed to beam.ParDo/beam.TryParDo to the number of emit parameters (or one plus the single returned value).","For multi-output DoFns, declare all outputs, e.g. pos, neg := beam.ParDo2(s, &fn{}, col).","If the DoFn returns a value, don't also add an emit param for the same output without an extra node."],"exampleFix":"// before: fn has 2 emit params but only 1 output declared\nout := beam.ParDo(s, &splitFn{}, col)\n\n// after: declare both outputs\npos, neg := beam.ParDo2(s, &splitFn{}, col)","handlingStrategy":"validation","validationCode":"emitCount := countEmitParams(fn)\nretCount := countReturnValues(fn)\nwantOutputs := emitCount + retCount\nif actualOutputs != wantOutputs {\n    return fmt.Errorf(\"DoFn %T declares %d outputs (emits %d, returns %d), got %d\", fn, wantOutputs, emitCount, retCount, actualOutputs)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use beam.ParDo2/beam.TryParDo2 for multi-output DoFns and declare every output.","Don't mix emit params and return values for the same output.","Adjust pipeline wiring immediately after editing DoFn signatures."],"tags":["go","apache-beam","dofn-signature","emit"],"backgroundTag":"missing-required-argument","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}