{"record":{"id":"3cfe03cbae9991c0","repo":"siyuan-note/siyuan","slug":"globalthis-siyuan-event-is-s","errorCode":null,"errorMessage":"globalThis.siyuan.event is %s","messagePattern":"globalThis\\.siyuan\\.event is (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/sandbox.go","lineNumber":341,"sourceCode":"\n// dispatchEvent calls the globalThis.siyuan.event.on hook with the given event object.\nfunc dispatchEvent(p *KernelPlugin, rt *goja.Runtime, e any) (async bool, err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = fmt.Errorf(\"goja panic during dispatchEvent: %v\", r)\n\t\t}\n\t}()\n\n\tevent, err := getJsContextValue(rt, []any{\"siyuan\", \"event\"})\n\tif err != nil {\n\t\treturn\n\t}\n\tif event == nil {\n\t\terr = fmt.Errorf(\"globalThis.siyuan.event not found\")\n\t\treturn\n\t}\n\tif goja.IsUndefined(event) || goja.IsNull(event) {\n\t\terr = fmt.Errorf(\"globalThis.siyuan.event is %s\", event.String())\n\t\treturn\n\t}\n\n\teventObj := event.ToObject(rt)\n\tif eventObj == nil {\n\t\terr = fmt.Errorf(\"globalThis.siyuan.event is not an object\")\n\t\treturn\n\t}\n\n\thandlerValue := eventObj.Get(\"handler\")\n\thandler, ok := goja.AssertFunction(handlerValue)\n\tif !ok {\n\t\treturn\n\t}\n\n\teventJs := rt.ToValue(e)\n\tinvokeResult, invokeErr := handler(event, eventJs)\n\tif invokeErr != nil {","sourceCodeStart":323,"sourceCodeEnd":359,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/plugin/sandbox.go#L323-L359","documentation":"dispatchEvent resolves globalThis.siyuan.event and rejects it when the value is JavaScript undefined or null, formatting the actual value into the message ('undefined' or 'null'). Unlike the 'not found' error (nil Go value), this means the property exists in the lookup sense but holds undefined/null at dispatch time.","triggerScenarios":"Plugin code assigns siyuan.event = undefined or null (explicitly or via a failed destructure/default), then the kernel dispatches an event through dispatchEvent.","commonSituations":"Teardown code clearing plugin state; a conditional init like `siyuan.event = someModule?.event` where someModule is undefined; plugin reload racing with event dispatch.","solutions":["Find and remove the assignment that sets siyuan.event to undefined/null in the plugin","Guard initialization so siyuan.event is always an object with a callable handler before the plugin reports readiness","If the plugin unloads, ensure it also stops receiving kernel events so dispatchEvent is not called against a torn-down context"],"exampleFix":"// before\nsiyuan.event = pluginModule && pluginModule.event; // undefined when pluginModule fails to load\n// after\nif (pluginModule && pluginModule.event) { siyuan.event = pluginModule.event; }","handlingStrategy":"validation","validationCode":"if (siyuan.event === undefined || siyuan.event === null) { siyuan.event = createEventBus(); }","typeGuard":"const isValidEventBus = (e) => e != null && typeof e === 'object' && typeof e.on === 'function';","tryCatchPattern":"if err := dispatchEvent(p, rt, e); err != nil { if strings.Contains(err.Error(), \"is undefined\") || strings.Contains(err.Error(), \"is null\") { reinitEventShim(rt) } }","preventionTips":["Never assign undefined/null to siyuan.event; only replace it with another valid event bus","Guard optional-chaining assignments that may yield undefined","On plugin unload, unregister listeners instead of nulling siyuan.event"],"tags":["goja","plugins","events","undefined"],"backgroundTag":"null-argument","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"}