siyuan-note/siyuan · error

worker event loop not initialized

Error message

worker event loop not initialized

What it means

Worker.Run executes a task on the goja event loop previously injected via Worker.Start(loop). If Run is called before Start, w.loop is nil and the worker cannot schedule onto the runtime, so it returns this error. It is an initialization-order bug in the worker's lifecycle.

Source

Thrown at kernel/plugin/worker.go:46

type TaskExecutor func(rt *goja.Runtime) (result any, err error)
type TaskCallback func(rt *goja.Runtime, result any, err error)

type Worker struct {
	loop *eventloop.EventLoop
}

type TaskResult struct {
	value any
	err   error
}

func (w *Worker) Start(loop *eventloop.EventLoop) {
	w.loop = loop
}

func (w *Worker) Run(executor TaskExecutor, callback TaskCallback) error {
	if w.loop == nil {
		return fmt.Errorf("worker event loop not initialized")
	}

	success := w.loop.RunOnLoop(func(rt *goja.Runtime) {
		var result any
		var err error

		defer func() {
			defer func() {
				// 捕获回调中的 panic 并保留原始调用栈,便于定位 Promise 处理异常。
				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)

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Call worker.Start(loop) with the plugin's eventloop.EventLoop before the first worker.Run call.
  2. Check plugin initialization order — ensure the JS runtime/eventloop bootstraps before any capability/hook/RPC dispatch.
  3. If the loop may legitimately be absent (plugin unloaded), check errors.Is/inspect the returned error and queue or reject the task gracefully.
  4. Audit teardown/reload paths so a Worker is re-Started with the new loop after a plugin reload.

Example fix

// before
w.Run(executor, cb) // loop never set
// after
w.Start(loop)
w.Run(executor, cb)
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: ensure the loop is set before scheduling
if w.loop == nil {
    return fmt.Errorf("worker event loop not initialized")
}

Try / catch

if err := w.Run(executor, callback); err != nil {
    // loop missing: initialize via w.Start(loop) and retry, or fail the task cleanly
}

Prevention

When it happens

Trigger: Calling worker.Run(executor, callback) without having called worker.Start(eventLoop) first; the runtime/eventloop was torn down (e.g. plugin unloaded) and the loop reference cleared or never set on a recreated Worker; two Worker instances created but Start called on the wrong one.

Common situations: Kernel code invoking agent capabilities, hooks, or RPC after plugin shutdown but before the worker is re-initialized; refactoring moved Start after the first Run; a nil loop leaked from a failed plugin boot.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/4b8c7bfdefb30160. Report an issue: GitHub.