siyuan-note/siyuan · error

globalThis.siyuan.plugin.lifecycle.%s not bound to a functio

Error message

globalThis.siyuan.plugin.lifecycle.%s not bound to a function

What it means

The kernel looks up the named hook (e.g. "onload", "onunload") on the lifecycle object and requires it to be a callable JS function via goja.AssertFunction. If the property is missing, undefined, or not a function, hook invocation fails with `globalThis.siyuan.plugin.lifecycle.%s not bound to a function` where %s is the hook name.

Source

Thrown at kernel/plugin/plugin.go:879

		lifecycle, err := getJsContextValue(rt, []any{"siyuan", "plugin", "lifecycle"})
		if err != nil {
			return
		}
		if lifecycle == nil {
			err = fmt.Errorf("globalThis.siyuan.plugin.lifecycle not found")
			return
		}

		pluginObj := lifecycle.ToObject(rt)
		if pluginObj == nil {
			err = fmt.Errorf("globalThis.siyuan.plugin.lifecycle is not an object")
			return
		}

		hookValue := pluginObj.Get(name)
		hook, ok := goja.AssertFunction(hookValue)
		if !ok {
			err = fmt.Errorf("globalThis.siyuan.plugin.lifecycle.%s not bound to a function", name)
			return
		}

		invokeFunction(func(_ *goja.Runtime, result *CallResult) {
			done <- *result.TaskResult()
		}, rt, true, hook, lifecycle)
		return
	}, func(_ *goja.Runtime, _ any, err error) {
		if err != nil {
			done <- TaskResult{err: err}
		}
	})
	if runErr != nil {
		done <- TaskResult{err: runErr}
	}

	result := <-done
	if result.err != nil {

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Define the missing hook as a function on the lifecycle object: `onload() {}` / `onunload() {}`
  2. Check the hook name casing exactly (kernel expects e.g. `onload`, not `onLoad`)
  3. Update the plugin to the current plugin-API hook names for the running kernel version
  4. Verify the bundler/minifier is not stripping or renaming lifecycle methods

Example fix

// before
class PluginLifecycle { onLoad() {} } // wrong casing
// after
class PluginLifecycle { onload() {} onunload() {} }
Defensive patterns

Strategy: type-guard

Validate before calling

// plugin JS self-check
for (const h of ['onload', 'onunload']) {
  if (typeof globalThis.siyuan.plugin.lifecycle[h] !== 'function') {
    console.warn(`missing lifecycle hook: ${h}`)
  }
}

Type guard

func hookIsFunction(rt *goja.Runtime, name string) bool {
  v, err := getJsContextValue(rt, []any{"siyuan", "plugin", "lifecycle"})
  if err != nil || v == nil { return false }
  obj := v.ToObject(rt)
  if obj == nil { return false }
  _, ok := goja.AssertFunction(obj.Get(name))
  return ok
}

Try / catch

if err := dispatchHook("onload"); err != nil {
  if strings.Contains(err.Error(), "not bound to a function") {
    logging.LogErrorf("plugin %s does not implement onload correctly", p.Name)
    // treat plugin as partially functional; skip hooks it lacks
  }
}

Prevention

When it happens

Trigger: Dispatching a named lifecycle hook when the plugin's lifecycle object lacks that method or defines it as a non-function (e.g. a property, undefined, or an arrow assigned to the wrong key).

Common situations: Plugin misses onload/onunload overrides; hook renamed in a newer kernel API while the plugin still uses the old name; minification stripping methods or a typo like `onLoad` vs `onload`.

Related errors


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