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
- Define the missing hook as a function on the lifecycle object: `onload() {}` / `onunload() {}`
- Check the hook name casing exactly (kernel expects e.g. `onload`, not `onLoad`)
- Update the plugin to the current plugin-API hook names for the running kernel version
- 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
- Define lifecycle members as methods with exact casing (onload, onunload)
- Check hook names against the current plugin-API docs on each kernel upgrade
- Configure the bundler to keep class method names (no aggressive mangling on lifecycle classes)
- Run a startup self-test that calls each hook in a dev environment
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
- globalThis.siyuan.plugin.lifecycle not found
- panic during lifecycle hook invocation: %v
- globalThis.siyuan.plugin.lifecycle is not an object
- injectAgent: %v
- registerCapability requires 3 arguments: name, config, handl
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/c572015bca0e91af.
Report an issue: GitHub.