{"record":{"id":"589fe95c958caf6c","repo":"siyuan-note/siyuan","slug":"globalthis-siyuan-event-is-not-an-object","errorCode":null,"errorMessage":"globalThis.siyuan.event is not an object","messagePattern":"globalThis\\.siyuan\\.event is not an object","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/plugin/sandbox.go","lineNumber":347,"sourceCode":"\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 {\n\t\terr = invokeErr\n\t\treturn\n\t}\n\n\tasync = isJsPromise(invokeResult)\n\treturn","sourceCodeStart":329,"sourceCodeEnd":365,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/plugin/sandbox.go#L329-L365","documentation":"dispatchEvent calls ToObject on the resolved globalThis.siyuan.event value and throws this error when that conversion returns nil. It is a defensive check: the value survived the nil/undefined/null checks but could not be converted to a goja object, so the event handler cannot be looked up.","triggerScenarios":"globalThis.siyuan.event holds a non-object that defeats ToObject in this goja version — practically only reachable via unusual host values or exotic wrapped values assigned to siyuan.event.","commonSituations":"Plugin code assigning a host-provided non-JS value (e.g. a Go value bridged without conversion) to siyuan.event; goja version-specific export/bridge quirks.","solutions":["Inspect what is assigned to globalThis.siyuan.event and replace it with a plain JS object exposing a handler/on function","When bridging Go values into the plugin runtime, wrap them with rt.ToValue / rt.NewObject instead of assigning raw host values","Add a startup assertion in the plugin: if (typeof siyuan.event !== 'object') throw new Error('invalid siyuan.event')"],"exampleFix":"// before\nsiyuan.event = hostBridgeValue; // raw Go-backed value\n// after\nsiyuan.event = { on: (name, cb) => hostBridgeSubscribe(name, cb) };","handlingStrategy":"type-guard","validationCode":"if (!siyuan.event || typeof siyuan.event !== 'object' || typeof siyuan.event.on !== 'function') { throw new Error('siyuan.event must be a plain object with an on function'); }","typeGuard":"const isEventObject = (v) => v != null && typeof v === 'object' && !Array.isArray(v);","tryCatchPattern":"if err := dispatchEvent(p, rt, e); err != nil { if strings.Contains(err.Error(), \"not an object\") { log.Errorf(\"plugin %s: bad siyuan.event value\", p.Name) } return }","preventionTips":["Assign only plain JS objects to siyuan.event, never raw host/Go-bridged values","Wrap bridged host functionality behind a plain object facade with function properties","Assert the shape of siyuan.event in the plugin's startup code"],"tags":["goja","plugins","type-mismatch"],"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"}