{"record":{"id":"694497e92706c341","repo":"vitessio/vitess","slug":"listener-must-take-exactly-one-input-argument","errorCode":null,"errorMessage":"listener must take exactly one input argument","messagePattern":"listener must take exactly one input argument","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/event/event.go","lineNumber":113,"sourceCode":"\treturn \"bad listener func: \" + string(why)\n}\n\n// AddListener registers a listener function that will be called when a matching\n// event is dispatched. The type of the function's first (and only) argument\n// declares the event type (or interface) to listen for.\nfunc AddListener(fn any) {\n\tlistenersMutex.Lock()\n\tdefer listenersMutex.Unlock()\n\n\tfnType := reflect.TypeOf(fn)\n\n\t// check that the function type is what we think: # of inputs/outputs, etc.\n\t// panic if conditions not met (because it's a programming error to have that happen)\n\tswitch {\n\tcase fnType.Kind() != reflect.Func:\n\t\tpanic(BadListenerError(\"listener must be a function\"))\n\tcase fnType.NumIn() != 1:\n\t\tpanic(BadListenerError(\"listener must take exactly one input argument\"))\n\t}\n\n\t// the first input parameter is the event\n\tevType := fnType.In(0)\n\n\t// keep a list of listeners for each event type\n\tlisteners[evType] = append(listeners[evType], fn)\n\n\t// if eventType is an interface, store it in a separate list\n\t// so we can check non-interface objects against all interfaces\n\tif evType.Kind() == reflect.Interface {\n\t\tinterfaces = append(interfaces, evType)\n\t}\n}\n\n// Dispatch sends an event to all registered listeners that were declared\n// to accept values of the event's type, or interfaces that the value implements.\nfunc Dispatch(ev any) {","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/event/event.go#L95-L131","documentation":"event.AddListener panics when a registered listener function does not take exactly one input parameter. Listeners are dispatched via reflection, so the framework requires a strict signature: a single argument (the event). This is a programming error, so the library panics at registration time rather than returning an error.","triggerScenarios":"Calling event.AddListener with a function whose signature has zero inputs, or two or more inputs (e.g. func(ev *Event, extra string) or func()).","commonSituations":"Writing a listener that also wants context, a sender parameter, or extra configuration arguments; accidentally passing the wrong function (e.g. a handler meant for another framework); refactoring a listener signature without updating AddListener calls.","solutions":["Change the listener function to accept exactly one parameter whose type is the event type.","If extra data is needed, capture it in a closure around a single-argument function.","Verify the value passed to AddListener is the function itself, not the result of calling it or a bound method with extra args."],"exampleFix":"// before\nevent.AddListener(func(ev *MyEvent, seq int) {\n    handle(ev, seq)\n})\n// after\nseq := 0\nevent.AddListener(func(ev *MyEvent) {\n    seq++\n    handle(ev, seq)\n})","handlingStrategy":"validation","validationCode":"fnVal := reflect.ValueOf(listener)\nif fnVal.Kind() != reflect.Func || fnVal.Type().NumIn() != 1 {\n    panic(fmt.Sprintf(\"listener %T must be a function with exactly one input\", listener))\n}\nevent.AddListener(listener)","typeGuard":"func isValidListener(fn interface{}) bool {\n    t := reflect.TypeOf(fn)\n    return t != nil && t.Kind() == reflect.Func && t.NumIn() == 1\n}","tryCatchPattern":null,"preventionTips":["Always write listeners as func(ev *YourEventType).","Use closures for any extra parameters instead of adding function arguments.","Add a unit test that registers each listener at startup so signature errors surface in CI."],"tags":["reflection","panics","api-misuse"],"backgroundTag":"invalid-listener-signature","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}