{"record":{"id":"5bbe7a07df3f8d8c","repo":"siyuan-note/siyuan","slug":"injectstorage-v","errorCode":null,"errorMessage":"injectStorage: %v","messagePattern":"injectStorage: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/api_storage.go","lineNumber":36,"sourceCode":"\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/dop251/goja\"\n\t\"github.com/samber/lo\"\n\t\"github.com/siyuan-note/filelock\"\n\t\"github.com/siyuan-note/logging\"\n\t\"github.com/siyuan-note/siyuan/kernel/util\"\n)\n\n// injectStorage adds siyuan.storage.* methods for scoped file CRUD.\nfunc injectStorage(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(\"injectStorage: %v\", r)\n\t\t}\n\t}()\n\n\tresolvePath := func(relPath string) (abs string, err error) {\n\t\tabs = filepath.Join(p.storageDir, filepath.Clean(relPath))\n\t\tif !(abs == p.storageDir || strings.HasPrefix(abs, p.storageDir+string(filepath.Separator))) {\n\t\t\terr = fmt.Errorf(\"siyuan.storage: path traversal not allowed\")\n\t\t}\n\t\treturn\n\t}\n\n\twatcher := rt.NewObject()\n\n\t// siyuan.storage.watcher.add(path) -> Promise<void>\n\tlo.Must0(watcher.Set(\"add\", rt.ToValue(func(call goja.FunctionCall, rt *goja.Runtime) goja.Value {\n\t\tpromise, resolve, reject := rt.NewPromise()\n\n\t\tvar argErr error","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/plugin/api_storage.go#L18-L54","documentation":"injectStorage registers the siyuan.storage.* file CRUD API into a kernel plugin's goja runtime. Any panic during this registration (lo.Must0 failing on Object.Set/ObjectFreeze, etc.) is recovered and returned as \"injectStorage: %v\", failing the plugin load. It signals a kernel-side injection failure, not a plugin script error.","triggerScenarios":"A panic inside injectStorage: one of the many lo.Must0 calls returns an error (e.g. setting a property fails because the object is sealed or the goja runtime is corrupted), or an unexpected panic occurs while wiring watcher/get/put/remove/list methods.","commonSituations":"Seen during plugin loading at SiYuan startup or when enabling a kernel plugin; typically caused by internal runtime state problems rather than anything the plugin author did.","solutions":["Read the wrapped panic message in the log to find the underlying cause","Restart SiYuan; broken runtime state during injection is usually transient","Upgrade SiYuan to the latest version to pick up fixes in the plugin subsystem","If it reproduces, isolate by disabling plugins and report the issue with logs"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Kernel-side Go code loading the plugin\nif err := injectStorage(p, rt, siyuan); err != nil {\n    return fmt.Errorf(\"plugin %s load failed: %w\", p.Name, err)\n}","preventionTips":["Restart SiYuan before deep debugging; the failure is often transient runtime state","Update SiYuan to the latest version to get injection fixes","Include full kernel logs when filing the issue"],"tags":["goja","plugin","kernel","panic","initialization"],"backgroundTag":"module-init-failed","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"}