{"record":{"id":"bc6785656b9db158","repo":"siyuan-note/siyuan","slug":"injectclient-v","errorCode":null,"errorMessage":"injectClient: %v","messagePattern":"injectClient: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/api_client.go","lineNumber":42,"sourceCode":"\t\"net/http\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\n\t\"github.com/dop251/goja\"\n\t\"github.com/lxzan/gws\"\n\tsse \"github.com/r3labs/sse/v2\"\n\t\"github.com/samber/lo\"\n\t\"github.com/siyuan-note/logging\"\n\t\"github.com/siyuan-note/siyuan/kernel/model\"\n\t\"github.com/siyuan-note/siyuan/kernel/util\"\n)\n\n// injectClient adds siyuan.server to the goja context.\nfunc injectClient(p *KernelPlugin, rt *goja.Runtime, siyuan *goja.Object) (err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = fmt.Errorf(\"injectClient: %v\", r)\n\t\t}\n\t}()\n\n\tclient := rt.NewObject()\n\n\tlo.Must0(client.Set(\"fetch\", rt.ToValue(func(call goja.FunctionCall, rt *goja.Runtime) goja.Value {\n\t\tpromise, resolve, reject := rt.NewPromise()\n\n\t\tvar argErr error\n\t\tvar path string\n\t\tmethod := \"GET\"\n\t\theaders := map[string]string{}\n\t\tvar bodyString *string\n\t\tvar bodyBytes *[]byte\n\n\t\tif goja.IsString(call.Argument(0)) {\n\t\t\tpath = call.Argument(0).String()\n\t\t} else {","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/plugin/api_client.go#L24-L60","documentation":"injectClient registers the siyuan.server object (fetch, SSE, WebSocket helpers) into the plugin's goja runtime. The whole function is wrapped in a recover() deferred handler so that any panic during injection — e.g. lo.Must0 failing on a Set error, rt.NewObject/NewPromise failures, or a runtime in a broken state — is converted into this error with the panic value embedded. It surfaces as plugin-load failure rather than crashing the kernel.","triggerScenarios":"A panic occurs while building the client object: goja runtime is closed or exhausted, a client.Set call errors (propagated by lo.Must0), or an internal invariant in the client setup (e.g. resolving the kernel HTTP endpoint) panics.","commonSituations":"Plugin worker runtime torn down mid-injection; port/endpoint resolution failure inside the client closure triggering a panic during setup; a bug/regression in the plugin host rather than in plugin code.","solutions":["Check kernel logs for the full panic stack alongside the 'injectClient: %v' message","Restart SiYuan and reload the plugin — the goja runtime is usually recreated fresh","Disable recently installed/updated plugins to isolate one corrupting the runtime","If reproducible, report with the panic value and plugin version; it is an internal-invariant failure, not a plugin API misuse"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// plugin host side (Go): injectClient already recovers; log and fail plugin load gracefully\nif err := injectClient(p, rt, siyuan); err != nil {\n    logging.LogErrorf(\"[plugin:%s] injectClient failed: %v\", p.Name, err)\n    return err\n}","preventionTips":["Keep the goja runtime alive and healthy during plugin load; avoid tearing down workers mid-injection","Check kernel logs for the panic value accompanying 'injectClient:'","Reproduce with a minimal plugin to rule out runtime state corruption","Update SiYuan — this indicates a host-side internal fault, not plugin code misuse"],"tags":["goja","panic","plugin-api","internal"],"backgroundTag":"internal-invariant-violation","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"}