siyuan-note/siyuan · error
failed to run task on event loop
Error message
failed to run task on event loop
What it means
The plugin Worker schedules a task (TaskExecutor) on a JS event loop (e.g. a GJavaScript/v8-style loop for plugin runtime). After running the callback, the deferred result is checked; if the executor set a non-nil error, Run() collapses it into the generic sentinel "failed to run task on event loop". It means the task itself failed while executing inside the event loop, not that the loop could not be reached.
Source
Thrown at kernel/plugin/worker.go:74
if r := recover(); r != nil {
logging.LogErrorf("task callback panicked: %v\n%s", r, debug.Stack())
}
}()
// 捕获执行器中的 panic 并保留原始调用栈,同时将错误传给回调。
if r := recover(); r != nil {
logging.LogErrorf("task executor panicked: %v\n%s", r, debug.Stack())
err = fmt.Errorf("task executor panicked: %v", r)
}
if callback != nil {
callback(rt, result, err)
}
}()
result, err = executor(rt)
})
if !success {
return fmt.Errorf("failed to run task on event loop")
}
return nil
}
func (w *Worker) RunSync(fn TaskExecutor) (result any, err error) {
response := make(chan TaskResult, 1)
err = w.Run(fn, func(rt *goja.Runtime, result any, err error) {
response <- TaskResult{result, err}
})
if err != nil {
close(response)
return
}
r := <-response
result = r.value
err = r.errView on GitHub (pinned to 8641553a1f)
Solutions
- Inspect kernel/plugin logs just before this error for the underlying error the executor produced; the generic message discards the original cause, so check the plugin's own console/log output
- Verify the plugin runtime was fully initialized (runtime loaded, entry script executed) before invoking Run
- If you wrote the hook/capability, fix the error it returns (nil check all runtime API results)
- If the executor needs the original error surfaced, change Run/TaskExecutor plumbing to wrap `err` (e.g. fmt.Errorf("failed to run task on event loop: %w", err)) instead of dropping it
Example fix
// before
if !success {
return fmt.Errorf("failed to run task on event loop")
}
// after
if !success {
if err != nil {
return fmt.Errorf("failed to run task on event loop: %w", err)
}
return fmt.Errorf("failed to run task on event loop")
} Defensive patterns
Strategy: try-catch
Validate before calling
// Go: check runtime readiness before dispatch
if w.runtime == nil || w.closed() {
return errors.New("plugin runtime not ready")
} Type guard
func (w *Worker) ready() bool { return w != nil && w.loop != nil } Try / catch
if err := worker.Run(executor); err != nil {
if strings.Contains(err.Error(), "failed to run task on event loop") {
// fall back to sync path or log plugin-side error
}
} Prevention
- Always initialize the plugin runtime before calling Run
- Have executors return wrapped, descriptive errors instead of bare failures
- Wrap err in the worker rather than discarding the cause
- Log plugin-side exceptions to correlate with this generic message
When it happens
Trigger: Calling Worker.Run(executor) where the executor function returns a non-nil error (success flag false after `result, err = executor(rt)` in kernel/plugin/worker.go:74). Callers include invokeAgentCapability, invokeHook, callRpcMethod, runtimeEventHandler and the HTTP handler path.
Common situations: A plugin hook or agent capability panics or returns an error inside the JS runtime; a runtime (rt) is in a bad state (closed loop, missing bindings); RPC method invoked on a plugin whose runtime threw while handling the call.
Related errors
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/2c45fd2c5c8f8ac0.
Report an issue: GitHub.