vitessio/vitess · error
listener must take exactly one input argument
Error message
listener must take exactly one input argument
What it means
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.
Source
Thrown at go/event/event.go:113
return "bad listener func: " + string(why)
}
// AddListener registers a listener function that will be called when a matching
// event is dispatched. The type of the function's first (and only) argument
// declares the event type (or interface) to listen for.
func AddListener(fn any) {
listenersMutex.Lock()
defer listenersMutex.Unlock()
fnType := reflect.TypeOf(fn)
// check that the function type is what we think: # of inputs/outputs, etc.
// panic if conditions not met (because it's a programming error to have that happen)
switch {
case fnType.Kind() != reflect.Func:
panic(BadListenerError("listener must be a function"))
case fnType.NumIn() != 1:
panic(BadListenerError("listener must take exactly one input argument"))
}
// the first input parameter is the event
evType := fnType.In(0)
// keep a list of listeners for each event type
listeners[evType] = append(listeners[evType], fn)
// if eventType is an interface, store it in a separate list
// so we can check non-interface objects against all interfaces
if evType.Kind() == reflect.Interface {
interfaces = append(interfaces, evType)
}
}
// Dispatch sends an event to all registered listeners that were declared
// to accept values of the event's type, or interfaces that the value implements.
func Dispatch(ev any) {View on GitHub (pinned to 01a25a7d17)
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.
Example fix
// before
event.AddListener(func(ev *MyEvent, seq int) {
handle(ev, seq)
})
// after
seq := 0
event.AddListener(func(ev *MyEvent) {
seq++
handle(ev, seq)
}) Defensive patterns
Strategy: validation
Validate before calling
fnVal := reflect.ValueOf(listener)
if fnVal.Kind() != reflect.Func || fnVal.Type().NumIn() != 1 {
panic(fmt.Sprintf("listener %T must be a function with exactly one input", listener))
}
event.AddListener(listener) Type guard
func isValidListener(fn interface{}) bool {
t := reflect.TypeOf(fn)
return t != nil && t.Kind() == reflect.Func && t.NumIn() == 1
} Prevention
- 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.
When it happens
Trigger: 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()).
Common situations: 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.
Related errors
- removing from wrong List
- invalid Coercibility value
- ReadFile cannot be called on read-write backup
- AddFile cannot be called on read-only backup
- EndBackup cannot be called on read-only backup
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/694497e92706c341.
Report an issue: GitHub.