{"record":{"id":"4017554ae1d80700","repo":"siyuan-note/siyuan","slug":"invalid-event-object","errorCode":null,"errorMessage":"invalid event object","messagePattern":"invalid event object","errorType":"exception","errorClass":"GoError","httpStatus":null,"severity":"error","filePath":"kernel/plugin/plugin.go","lineNumber":1404,"sourceCode":"\t\t\t\t\t}\n\n\t\t\t\t\tif event := eventObj.Get(\"event\"); goja.IsString(event) {\n\t\t\t\t\t\te.Event = event.String()\n\t\t\t\t\t}\n\n\t\t\t\t\tif id := eventObj.Get(\"id\"); goja.IsString(id) {\n\t\t\t\t\t\te.Id = id.String()\n\t\t\t\t\t}\n\n\t\t\t\t\tif retry := eventObj.Get(\"retry\"); goja.IsNumber(retry) {\n\t\t\t\t\t\te.Retry = uint(retry.ToInteger())\n\t\t\t\t\t}\n\n\t\t\t\t\tevents.In <- e\n\t\t\t\t\treturn goja.Undefined()\n\t\t\t\t}\n\t\t\t}\n\t\t\tpanic(rt.NewGoError(fmt.Errorf(\"invalid event object\")))\n\t\t})\n\n\t\tport_close := rt.ToValue(func(call goja.FunctionCall, rt *goja.Runtime) goja.Value {\n\t\t\tdoClose()\n\t\t\treturn goja.Undefined()\n\t\t})\n\n\t\tlo.Must0(port.Set(\"onopen\", goja.Null()))\n\t\tlo.Must0(port.Set(\"onclose\", goja.Null()))\n\n\t\tlo.Must0(port.Set(\"send\", port_send))\n\t\tlo.Must0(port.Set(\"close\", port_close))\n\n\t\tlo.Must0(ObjectSeal(rt, port))\n\n\t\tlo.Must0(jsRequestObj.Set(\"port\", port))\n\n\t\tinvokeFunction(func(_ *goja.Runtime, result *CallResult) {","sourceCodeStart":1386,"sourceCodeEnd":1422,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/plugin/plugin.go#L1386-L1422","documentation":"The SSE port's send function falls through to panic with 'invalid event object' when its first argument is null/undefined or cannot be converted to a JS object. Unlike 'event.data is required' (object present but missing data), this error means send() did not receive a usable object at all.","triggerScenarios":"Calling port.send() with no arguments; port.send(null) or port.send(undefined); passing a primitive (number/string/boolean) where an event object is expected and ToObject yields nil; passing a value that is not null-checked before ToObject.","commonSituations":"Async code paths that pass a possibly-empty result variable into port.send; spreading/destructuring mistakes that yield undefined; API misuse after reading examples of the fetch-style API instead of the SSE port API.","solutions":["Always pass a plain object to port.send: port.send({data: ...})","Guard the call site: if (payload) port.send({data: payload})","Ensure variables hold the event object, not a resolved inner value that may be undefined","Wrap sends in try/catch so a transient bad payload does not kill the SSE handler"],"exampleFix":"// before\nconst result = await compute();\nport.send(result);\n// after\nconst result = await compute();\nif (result != null) { port.send({ data: JSON.stringify(result) }); }","handlingStrategy":"validation","validationCode":"function assertEventObject(ev) { if (ev === null || ev === undefined || typeof ev !== \"object\") throw new Error(\"port.send requires an event object\"); }","typeGuard":"const isEventObject = (v) => v !== null && typeof v === \"object\";","tryCatchPattern":"try { port.send(ev); } catch (e) { if (String(e).includes(\"invalid event object\")) { port.send({ data: String(ev) }); } else { throw e; } }","preventionTips":["Guard possibly-empty async results before passing them to port.send","Never call port.send with zero arguments, primitives, or null","Centralize sends in one wrapper that normalizes payloads to {data: ...}","Test SSE handlers with empty and error results to catch undefined payloads"],"tags":["sse","plugin-api","validation","invalid-argument"],"backgroundTag":"invalid-argument-value","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}