{"record":{"id":"9097a0e1a81d2988","repo":"siyuan-note/siyuan","slug":"panic-during-siyuan-client-event-v","errorCode":null,"errorMessage":"panic during siyuan.client.event: %v","messagePattern":"panic during siyuan\\.client\\.event: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/api_client.go","lineNumber":697,"sourceCode":"\t\t\tlo.Must0(esObj.Set(\"readyState\", rt.ToValue(readyState.Load())))\n\t\t\tlo.Must0(esObj.Set(\"url\", rt.ToValue(path)))\n\n\t\t\tlo.Must0(esObj.Set(\"onopen\", goja.Null()))\n\t\t\tlo.Must0(esObj.Set(\"onmessage\", goja.Null()))\n\t\t\tlo.Must0(esObj.Set(\"onclose\", goja.Null()))\n\t\t\tlo.Must0(esObj.Set(\"onerror\", goja.Null()))\n\n\t\t\tlo.Must0(esObj.Set(\"close\", es_close))\n\n\t\t\tlo.Must0(ObjectSeal(rt, esObj))\n\n\t\t\tsetReadyState(EventSourceConnecting)\n\n\t\t\tgo func() {\n\t\t\t\tvar err error\n\t\t\t\tdefer func() {\n\t\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\t\terr = fmt.Errorf(\"panic during siyuan.client.event: %v\", r)\n\t\t\t\t\t}\n\n\t\t\t\t\tdoClose()\n\n\t\t\t\t\tp.worker.Run(func(rt *goja.Runtime) (_ any, _ error) {\n\t\t\t\t\t\tif err != nil && !errors.Is(err, context.Canceled) {\n\t\t\t\t\t\t\tevent := rt.NewObject()\n\t\t\t\t\t\t\tevent.Set(\"type\", rt.ToValue(\"error\"))\n\t\t\t\t\t\t\tevent.Set(\"error\", rt.NewGoError(err))\n\t\t\t\t\t\t\tinvokeEsHook(\"onerror\", event)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif EventSourceState(readyState.Load()) != EventSourceClosed {\n\t\t\t\t\t\t\tsetReadyState(EventSourceClosed)\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn\n\t\t\t\t\t}, nil)\n\t\t\t\t}()\n","sourceCodeStart":679,"sourceCodeEnd":715,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/plugin/api_client.go#L679-L715","documentation":"The siyuan.client.event stream runs its connection loop in a background Go goroutine. Any panic in that loop is recovered, the stream is closed, and the panic is converted to this error and surfaced to the plugin's JS error callback. It signals an internal defect or unexpected runtime condition in the event-stream machinery, not a normal plugin API error.","triggerScenarios":"A panic inside the event-stream read/dispatch goroutine: nil dereference while processing a pushed event, unexpected close/reconnect ordering, or abnormal termination of the underlying HTTP stream.","commonSituations":"Kernel bugs during event dispatch; races when the plugin closes the stream while events are in flight; stress-testing event-driven plugins.","solutions":["Inspect the wrapped %v value in the message to identify the underlying panic and report it upstream with a reproduction","Minimize the event handler and reconnect to isolate which topic/event triggers it","Handle the stream error callback and reconnect defensively so a dropped stream does not break the plugin","Update to the latest kernel version in case the panic is an already-fixed internal bug"],"exampleFix":"const es = siyuan.client.event('/events/push',\n  (ev) => { try { handle(ev); } catch (e) { console.error(e); } },\n  (err) => { console.error('event stream failed:', err); scheduleReconnect(); });","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"null","tryCatchPattern":"const es = siyuan.client.event(streamPath, onEvent, (err) => {\n  if (String(err.message).startsWith('panic during siyuan.client.event')) {\n    console.error('Kernel event stream panicked:', err.message);\n    setTimeout(reconnect, backoff++ * 1000);\n  }\n});","preventionTips":["Keep event handlers defensive (try/catch inside handlers) so failures are contained","Implement reconnect with backoff on stream errors","Keep the kernel updated; report panics with reproduction steps"],"tags":["panic","eventsource","goroutine","goja"],"backgroundTag":"internal-invariant-violation","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"}