siyuan-note/siyuan · error · GoError
event.data is required
Error message
event.data is required
What it means
The SSE port's send(event) function requires the event object to carry a data field. If port.send is called with an object that has no 'data' property (or data is undefined), the kernel panics the JS call with a GoError 'event.data is required'. Event fields event, id, and retry are optional, but data is mandatory for an SSE stream frame.
Solutions
- Always include a data property in the object passed to port.send, even if empty: {data: ''}
- Guard the call: if (event && event.data !== undefined) port.send(event)
- Fix renamed fields so the payload is under data, not message/payload/body
- Serialize non-string payloads yourself if you need a specific wire format (the kernel exports data as-is)
Example fix
// before
port.send({ event: "progress" });
// after
port.send({ event: "progress", data: JSON.stringify({ percent: 50 }) }); Defensive patterns
Strategy: validation
Validate before calling
function assertSseEvent(ev) { if (!ev || typeof ev !== "object") throw new Error("send requires an event object"); if (ev.data === undefined) throw new Error("event.data is required"); } Type guard
const hasSseData = (ev) => !!ev && typeof ev === "object" && ev.data !== undefined;
Try / catch
try { port.send(ev); } catch (e) { if (String(e).includes("event.data is required")) { port.send({ ...ev, data: "" }); } else { throw e; } } Prevention
- Always set data in every event object passed to port.send
- Keep the canonical field name data; do not rename to payload/message
- Write a helper builder (makeEvent(data, opts)) so ad-hoc objects are not constructed
- Add unit tests asserting each emitted event includes data
When it happens
Trigger: Calling port.send({event: 'log'}) or port.send({}) without a data key; passing an object whose data was conditionally assigned and ended up undefined; calling port.send(undefined/null plus a non-object) is a different error (invalid event object), this one is specifically an object without data.
Common situations: Plugin authors building events dynamically and forgetting to set data; refactors renaming data to payload/message while keeping port.send; forwarding upstream objects that lack data.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- invalid event object
- createDocTree document title is required
- each key requires name and type
- Field [conf] is required
- Field [id] should be of type [String]
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/4f7384663d2bb0cc.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/plugin/plugin.go:1385
invokePortHook := func(name string, args ...goja.Value) {
hook := port.Get(name)
if fn, ok := goja.AssertFunction(hook); ok {
if _, callErr := fn(port, args...); callErr != nil {
logging.LogErrorf("[plugin:%s] sse server port hook %q: %v", p.Name, name, callErr)
}
}
}
port_send := rt.ToValue(func(call goja.FunctionCall, rt *goja.Runtime) goja.Value {
if eventJs := call.Argument(0); isJsValueNotNull(eventJs) {
if eventObj := eventJs.ToObject(rt); eventObj != nil {
e := sse.Event{}
if data := eventObj.Get("data"); isJsValueNotUndefined(data) {
e.Data = data.Export()
} else {
panic(rt.NewGoError(fmt.Errorf("event.data is required")))
}
if event := eventObj.Get("event"); goja.IsString(event) {
e.Event = event.String()
}
if id := eventObj.Get("id"); goja.IsString(id) {
e.Id = id.String()
}
if retry := eventObj.Get("retry"); goja.IsNumber(retry) {
e.Retry = uint(retry.ToInteger())
}
events.In <- e
return goja.Undefined()
}
}View on GitHub (pinned to 9f775e8a12)