{"record":{"id":"c572015bca0e91af","repo":"siyuan-note/siyuan","slug":"globalthis-siyuan-plugin-lifecycle-s-not-bound-to","errorCode":null,"errorMessage":"globalThis.siyuan.plugin.lifecycle.%s not bound to a function","messagePattern":"globalThis\\.siyuan\\.plugin\\.lifecycle\\.(.+?) not bound to a function","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/plugin.go","lineNumber":879,"sourceCode":"\t\tlifecycle, err := getJsContextValue(rt, []any{\"siyuan\", \"plugin\", \"lifecycle\"})\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif lifecycle == nil {\n\t\t\terr = fmt.Errorf(\"globalThis.siyuan.plugin.lifecycle not found\")\n\t\t\treturn\n\t\t}\n\n\t\tpluginObj := lifecycle.ToObject(rt)\n\t\tif pluginObj == nil {\n\t\t\terr = fmt.Errorf(\"globalThis.siyuan.plugin.lifecycle is not an object\")\n\t\t\treturn\n\t\t}\n\n\t\thookValue := pluginObj.Get(name)\n\t\thook, ok := goja.AssertFunction(hookValue)\n\t\tif !ok {\n\t\t\terr = fmt.Errorf(\"globalThis.siyuan.plugin.lifecycle.%s not bound to a function\", name)\n\t\t\treturn\n\t\t}\n\n\t\tinvokeFunction(func(_ *goja.Runtime, result *CallResult) {\n\t\t\tdone <- *result.TaskResult()\n\t\t}, rt, true, hook, lifecycle)\n\t\treturn\n\t}, func(_ *goja.Runtime, _ any, err error) {\n\t\tif err != nil {\n\t\t\tdone <- TaskResult{err: err}\n\t\t}\n\t})\n\tif runErr != nil {\n\t\tdone <- TaskResult{err: runErr}\n\t}\n\n\tresult := <-done\n\tif result.err != nil {","sourceCodeStart":861,"sourceCodeEnd":897,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/plugin/plugin.go#L861-L897","documentation":"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.","triggerScenarios":"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).","commonSituations":"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`.","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"],"exampleFix":"// before\nclass PluginLifecycle { onLoad() {} } // wrong casing\n// after\nclass PluginLifecycle { onload() {} onunload() {} }","handlingStrategy":"type-guard","validationCode":"// plugin JS self-check\nfor (const h of ['onload', 'onunload']) {\n  if (typeof globalThis.siyuan.plugin.lifecycle[h] !== 'function') {\n    console.warn(`missing lifecycle hook: ${h}`)\n  }\n}","typeGuard":"func hookIsFunction(rt *goja.Runtime, name string) bool {\n  v, err := getJsContextValue(rt, []any{\"siyuan\", \"plugin\", \"lifecycle\"})\n  if err != nil || v == nil { return false }\n  obj := v.ToObject(rt)\n  if obj == nil { return false }\n  _, ok := goja.AssertFunction(obj.Get(name))\n  return ok\n}","tryCatchPattern":"if err := dispatchHook(\"onload\"); err != nil {\n  if strings.Contains(err.Error(), \"not bound to a function\") {\n    logging.LogErrorf(\"plugin %s does not implement onload correctly\", p.Name)\n    // treat plugin as partially functional; skip hooks it lacks\n  }\n}","preventionTips":["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"],"tags":["plugin","goja","lifecycle","javascript"],"backgroundTag":"method-not-implemented","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}