{"record":{"id":"222df829404f838b","repo":"siyuan-note/siyuan","slug":"path-required-222df8","errorCode":null,"errorMessage":"path required","messagePattern":"path required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/api_storage.go","lineNumber":59,"sourceCode":"\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\n\t\tvar path string\n\t\tif len(call.Arguments) >= 1 && goja.IsString(call.Argument(0)) {\n\t\t\tpath = call.Argument(0).String()\n\t\t} else {\n\t\t\targErr = fmt.Errorf(\"path required\")\n\t\t}\n\n\t\trunErr := p.worker.Run(func(rt *goja.Runtime) (result any, err error) {\n\t\t\tif argErr != nil {\n\t\t\t\terr = argErr\n\t\t\t\treturn\n\t\t\t}\n\t\t\tabs, resolveErr := resolvePath(path)\n\t\t\tif resolveErr != nil {\n\t\t\t\terr = resolveErr\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif addErr := p.addStorageWatch(abs); addErr != nil {\n\t\t\t\terr = fmt.Errorf(\"failed to add storage path to watcher: %v\", addErr)\n\t\t\t}\n\t\t\treturn\n\t\t}, func(rt *goja.Runtime, result any, err error) {\n\t\t\tif lo.IsNil(err) {","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/plugin/api_storage.go#L41-L77","documentation":"siyuan.storage methods require the target path as a string first argument. When the argument is missing or is not a string, the promise rejects with 'path required'. The check is len(call.Arguments) >= 1 && goja.IsString(...), so undefined, null, and non-strings all fail identically.","triggerScenarios":"Calling storage.get(), storage.set(), storage.remove(), etc. with no argument, or with a non-string first argument such as an object, number, or null.","commonSituations":"Forgetting the path when only data was meant (e.g. set(value) instead of set(key, value)); a key variable that is undefined because an earlier lookup failed; numeric IDs passed without String() conversion.","solutions":["Pass the relative path as a string first argument: storage.get('data.json')","For set/remove, ensure the key/path argument is not accidentally omitted or shifted","Coerce dynamic keys with String(key) after verifying they are defined","Guard call sites: if (!path || typeof path !== 'string') return before calling"],"exampleFix":"// before\nawait siyuan.storage.set({ count: 1 })\n// after\nawait siyuan.storage.set('state.json', JSON.stringify({ count: 1 }))","handlingStrategy":"validation","validationCode":"if (typeof path !== 'string' || !path) throw new TypeError('storage path required')","typeGuard":"const hasPath = (v) => typeof v === 'string' && v.length > 0","tryCatchPattern":"try { await siyuan.storage.get(path) } catch (e) { if (e.message === 'path required') { /* argument missing or non-string */ } throw e }","preventionTips":["Verify argument order for set(key, value) style calls","Coerce numeric IDs to strings before use","Default-fill missing path parameters at the wrapper level"],"tags":["javascript","storage","missing-argument"],"backgroundTag":"missing-required-argument","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"}