GopeedLab/gopeed · error
missing handler
Error message
missing handler
What it means
Thrown by the event-registration bridge in newJSEventsRuntime (extension_runtime_webview.go:74-87) when a gopeed.events listener is registered with zero arguments. All of onResolve, onStart, onError and onDone route through the same register closure, which requires exactly one callable handler argument; an empty call panics immediately as a JS exception inside the extension script.
Source
Thrown at pkg/download/extension_runtime_webview.go:78
})
_ = obj.Set("revokeObjectURL", func(call goja.FunctionCall) goja.Value {
fn, ok := goja.AssertFunction(vm.Get("__gopeed_blob_revoke_object_url"))
if !ok {
panic(vm.ToValue(fmt.Errorf("blob runtime is not available")))
}
if _, err := fn(goja.Undefined(), call.Argument(0)); err != nil {
panic(vm.ToValue(err))
}
return goja.Undefined()
})
return obj
}
func newJSEventsRuntime(vm *goja.Runtime, events InstanceEvents) *goja.Object {
obj := vm.NewObject()
register := func(event ActivationEvent, call goja.FunctionCall) goja.Value {
if len(call.Arguments) == 0 {
panic(vm.ToValue(fmt.Errorf("missing handler")))
}
fnValue := call.Argument(0)
exported, ok := fnValue.Export().(func(goja.FunctionCall) goja.Value)
if !ok {
panic(vm.ToValue(fmt.Errorf("handler must be a function")))
}
events.register(event, engine.JSFunction(exported))
return goja.Undefined()
}
_ = obj.Set("onResolve", func(call goja.FunctionCall) goja.Value {
return register(EventOnResolve, call)
})
_ = obj.Set("onStart", func(call goja.FunctionCall) goja.Value {
return register(EventOnStart, call)
})
_ = obj.Set("onError", func(call goja.FunctionCall) goja.Value {
return register(EventOnError, call)
})View on GitHub (pinned to 7b7327ffb3)
Solutions
- Pass a function as the first argument: gopeed.events.onDone(handler)
- If the handler comes from a variable, verify it is defined and callable before registering
- Check the extension log/stack trace for the exact registration site named in the panic
Example fix
// before
gopeed.events.onDone();
// after
gopeed.events.onDone((task) => {
gopeed.logger.info(`task finished: ${task.id}`);
}); Defensive patterns
Strategy: validation
Validate before calling
if (arguments.length === 0 || typeof handler === "undefined") {
throw new TypeError("event handler is required");
}
gopeed.events.onDone(handler); Type guard
function hasHandler(fn) {
return typeof fn === "function";
} Prevention
- Register handlers with inline arrow functions so the argument is never omitted
- Lint extension code with no-unused-expressions rules that flag bare method calls
- Centralize event registration in one helper that validates its arguments
When it happens
Trigger: Calling gopeed.events.onResolve(), onStart(), onError() or onDone() with no arguments, e.g. a forgotten callback while refactoring, a handler variable that was never defined so the call site ends up empty, or generated/glued code that emits the bare registration call.
Common situations: Copy-pasting an event registration template and deleting the handler; minifiers or code generators that drop unused function arguments; typos like gopeed.events.onDone; calling registration inside a loop where the handler array is empty.
Related errors
AI-assisted analysis of GopeedLab/gopeed@7b7327ffb3 (2026-08-16).
Data as JSON: /api/errors/96d54e8354e599c3.
Report an issue: GitHub.