{"record":{"id":"26fe95f2cd34aaf1","repo":"siyuan-note/siyuan","slug":"globalthis-siyuan-plugin-lifecycle-is-not-an-objec","errorCode":null,"errorMessage":"globalThis.siyuan.plugin.lifecycle is not an object","messagePattern":"globalThis\\.siyuan\\.plugin\\.lifecycle is not an object","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/plugin.go","lineNumber":872,"sourceCode":"\t\t\tlogging.LogErrorf(\"[plugin:%s] lifecycle hook [%q] error: %v\", p.Name, name, err)\n\t\t}\n\t}()\n\n\tdone := make(chan TaskResult, 1)\n\n\trunErr := p.worker.Run(func(rt *goja.Runtime) (_ any, err error) {\n\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}","sourceCodeStart":854,"sourceCodeEnd":890,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/plugin/plugin.go#L854-L890","documentation":"After resolving globalThis.siyuan.plugin.lifecycle, the kernel calls ToObject(rt); if goja yields nil the value is not a usable object, and hook dispatch aborts with this error. This guards against the lifecycle slot being a non-object (e.g. null, a number, a boolean).","triggerScenarios":"Plugin code assigning a non-object to globalThis.siyuan.plugin.lifecycle (e.g. `null`, `undefined` assigned after check, a primitive), so lifecycle.ToObject(rt) returns nil.","commonSituations":"Typo like `siyuan.plugin.lifecycle = null` during an init reset; minified/bundled plugin overwriting the slot; a plugin that exports a class but never instantiates it into the slot.","solutions":["Inspect what the plugin assigns to globalThis.siyuan.plugin.lifecycle; make it a plain object or class instance","Search the plugin bundle for reassignments/overwrites of the lifecycle slot during init","Ensure asynchronous init does not null the slot before the kernel invokes hooks","Use the official plugin template so the lifecycle object is constructed correctly"],"exampleFix":"// before\nglobalThis.siyuan.plugin.lifecycle = null // reset in some init path\n// after\nglobalThis.siyuan.plugin.lifecycle = globalThis.siyuan.plugin.lifecycle || new PluginLifecycle()","handlingStrategy":"type-guard","validationCode":"// plugin JS before handing off\nconst lc = globalThis.siyuan?.plugin?.lifecycle\nif (typeof lc !== 'object' || lc === null) {\n  throw new Error('siyuan.plugin.lifecycle must be an object')\n}","typeGuard":"func isLifecycleObject(rt *goja.Runtime) bool {\n  v, err := getJsContextValue(rt, []any{\"siyuan\", \"plugin\", \"lifecycle\"})\n  return err == nil && v != nil && v.ToObject(rt) != nil\n}","tryCatchPattern":"if err := dispatchHook(name); err != nil {\n  if strings.Contains(err.Error(), \"lifecycle is not an object\") {\n    logging.LogErrorf(\"plugin %s assigned non-object to lifecycle slot\", p.Name)\n  }\n}","preventionTips":["Assign an object (or class instance) to globalThis.siyuan.plugin.lifecycle, never a primitive or null","Search bundled plugin code for later reassignments of the lifecycle slot","Avoid async init paths that can null the slot before hooks fire","Lint plugin entry code for `lifecycle =` assignments"],"tags":["plugin","goja","type-mismatch","lifecycle"],"backgroundTag":"type-mismatch","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}